blob: 0c5d907ff1d6dc1084339e007ff6f708ba1e85ab [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
Brad Bishopd7bf8c12018-02-25 22:55:05 -050020 bb.note("Executing '%s' ..." % index_cmd)
21 result = subprocess.check_output(index_cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050022 if result:
23 bb.note(result)
24
Patrick Williamsc0f7c042017-02-23 20:41:17 -060025"""
26This method parse the output from the package managerand return
27a dictionary with the information of the packages. This is used
28when the packages are in deb or ipk format.
29"""
30def opkg_query(cmd_output):
31 verregex = re.compile(' \([=<>]* [^ )]*\)')
32 output = dict()
33 pkg = ""
34 arch = ""
35 ver = ""
36 filename = ""
37 dep = []
38 pkgarch = ""
39 for line in cmd_output.splitlines():
40 line = line.rstrip()
41 if ':' in line:
42 if line.startswith("Package: "):
43 pkg = line.split(": ")[1]
44 elif line.startswith("Architecture: "):
45 arch = line.split(": ")[1]
46 elif line.startswith("Version: "):
47 ver = line.split(": ")[1]
48 elif line.startswith("File: ") or line.startswith("Filename:"):
49 filename = line.split(": ")[1]
50 if "/" in filename:
51 filename = os.path.basename(filename)
52 elif line.startswith("Depends: "):
53 depends = verregex.sub('', line.split(": ")[1])
54 for depend in depends.split(", "):
55 dep.append(depend)
56 elif line.startswith("Recommends: "):
57 recommends = verregex.sub('', line.split(": ")[1])
58 for recommend in recommends.split(", "):
59 dep.append("%s [REC]" % recommend)
60 elif line.startswith("PackageArch: "):
61 pkgarch = line.split(": ")[1]
Patrick Williamsc124f4f2015-09-15 14:41:29 -050062
Patrick Williamsc0f7c042017-02-23 20:41:17 -060063 # When there is a blank line save the package information
64 elif not line:
65 # IPK doesn't include the filename
66 if not filename:
67 filename = "%s_%s_%s.ipk" % (pkg, ver, arch)
68 if pkg:
69 output[pkg] = {"arch":arch, "ver":ver,
70 "filename":filename, "deps": dep, "pkgarch":pkgarch }
71 pkg = ""
72 arch = ""
73 ver = ""
74 filename = ""
75 dep = []
76 pkgarch = ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -050077
Patrick Williamsc0f7c042017-02-23 20:41:17 -060078 if pkg:
79 if not filename:
80 filename = "%s_%s_%s.ipk" % (pkg, ver, arch)
81 output[pkg] = {"arch":arch, "ver":ver,
82 "filename":filename, "deps": dep }
83
84 return output
85
86
87class Indexer(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050088 def __init__(self, d, deploy_dir):
89 self.d = d
90 self.deploy_dir = deploy_dir
91
92 @abstractmethod
93 def write_index(self):
94 pass
95
96
97class RpmIndexer(Indexer):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050098 def write_index(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050099 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500100 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
101 else:
102 signer = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500103
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500104 createrepo_c = bb.utils.which(os.environ['PATH'], "createrepo_c")
105 result = create_index("%s --update -q %s" % (createrepo_c, self.deploy_dir))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500106 if result:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500107 bb.fatal(result)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500108
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500109 # Sign repomd
110 if signer:
111 sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
112 is_ascii_sig = (sig_type.upper() != "BIN")
113 signer.detach_sign(os.path.join(self.deploy_dir, 'repodata', 'repomd.xml'),
114 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
115 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
116 armor=is_ascii_sig)
117
118
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500119class OpkgIndexer(Indexer):
120 def write_index(self):
121 arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS",
122 "SDK_PACKAGE_ARCHS",
123 "MULTILIB_ARCHS"]
124
125 opkg_index_cmd = bb.utils.which(os.getenv('PATH'), "opkg-make-index")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500126 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
127 signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND'))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500128 else:
129 signer = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500130
131 if not os.path.exists(os.path.join(self.deploy_dir, "Packages")):
132 open(os.path.join(self.deploy_dir, "Packages"), "w").close()
133
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500134 index_cmds = set()
135 index_sign_files = set()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500136 for arch_var in arch_vars:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500137 archs = self.d.getVar(arch_var)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500138 if archs is None:
139 continue
140
141 for arch in archs.split():
142 pkgs_dir = os.path.join(self.deploy_dir, arch)
143 pkgs_file = os.path.join(pkgs_dir, "Packages")
144
145 if not os.path.isdir(pkgs_dir):
146 continue
147
148 if not os.path.exists(pkgs_file):
149 open(pkgs_file, "w").close()
150
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500151 index_cmds.add('%s -r %s -p %s -m %s' %
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500152 (opkg_index_cmd, pkgs_file, pkgs_file, pkgs_dir))
153
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500154 index_sign_files.add(pkgs_file)
155
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500156 if len(index_cmds) == 0:
157 bb.note("There are no packages in %s!" % self.deploy_dir)
158 return
159
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500160 oe.utils.multiprocess_exec(index_cmds, create_index)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500161
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500162 if signer:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500163 feed_sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500164 is_ascii_sig = (feed_sig_type.upper() != "BIN")
165 for f in index_sign_files:
166 signer.detach_sign(f,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500167 self.d.getVar('PACKAGE_FEED_GPG_NAME'),
168 self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'),
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500169 armor=is_ascii_sig)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500170
171
172class DpkgIndexer(Indexer):
173 def _create_configs(self):
174 bb.utils.mkdirhier(self.apt_conf_dir)
175 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "lists", "partial"))
176 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "apt.conf.d"))
177 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "preferences.d"))
178
179 with open(os.path.join(self.apt_conf_dir, "preferences"),
180 "w") as prefs_file:
181 pass
182 with open(os.path.join(self.apt_conf_dir, "sources.list"),
183 "w+") as sources_file:
184 pass
185
186 with open(self.apt_conf_file, "w") as apt_conf:
187 with open(os.path.join(self.d.expand("${STAGING_ETCDIR_NATIVE}"),
188 "apt", "apt.conf.sample")) as apt_conf_sample:
189 for line in apt_conf_sample.read().split("\n"):
190 line = re.sub("#ROOTFS#", "/dev/null", line)
191 line = re.sub("#APTCONF#", self.apt_conf_dir, line)
192 apt_conf.write(line + "\n")
193
194 def write_index(self):
195 self.apt_conf_dir = os.path.join(self.d.expand("${APTCONF_TARGET}"),
196 "apt-ftparchive")
197 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
198 self._create_configs()
199
200 os.environ['APT_CONFIG'] = self.apt_conf_file
201
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500202 pkg_archs = self.d.getVar('PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500203 if pkg_archs is not None:
204 arch_list = pkg_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500205 sdk_pkg_archs = self.d.getVar('SDK_PACKAGE_ARCHS')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500206 if sdk_pkg_archs is not None:
207 for a in sdk_pkg_archs.split():
208 if a not in pkg_archs:
209 arch_list.append(a)
210
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500211 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500212 arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in arch_list)
213
214 apt_ftparchive = bb.utils.which(os.getenv('PATH'), "apt-ftparchive")
215 gzip = bb.utils.which(os.getenv('PATH'), "gzip")
216
217 index_cmds = []
218 deb_dirs_found = False
219 for arch in arch_list:
220 arch_dir = os.path.join(self.deploy_dir, arch)
221 if not os.path.isdir(arch_dir):
222 continue
223
224 cmd = "cd %s; PSEUDO_UNLOAD=1 %s packages . > Packages;" % (arch_dir, apt_ftparchive)
225
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500226 cmd += "%s -fcn Packages > Packages.gz;" % gzip
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500227
228 with open(os.path.join(arch_dir, "Release"), "w+") as release:
229 release.write("Label: %s\n" % arch)
230
231 cmd += "PSEUDO_UNLOAD=1 %s release . >> Release" % apt_ftparchive
232
233 index_cmds.append(cmd)
234
235 deb_dirs_found = True
236
237 if not deb_dirs_found:
238 bb.note("There are no packages in %s" % self.deploy_dir)
239 return
240
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500241 oe.utils.multiprocess_exec(index_cmds, create_index)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500242 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500243 raise NotImplementedError('Package feed signing not implementd for dpkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500244
245
246
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600247class PkgsList(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500248 def __init__(self, d, rootfs_dir):
249 self.d = d
250 self.rootfs_dir = rootfs_dir
251
252 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500253 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500254 pass
255
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500256class RpmPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500257 def list_pkgs(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500258 return RpmPM(self.d, self.rootfs_dir, self.d.getVar('TARGET_VENDOR')).list_installed()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500259
260class OpkgPkgsList(PkgsList):
261 def __init__(self, d, rootfs_dir, config_file):
262 super(OpkgPkgsList, self).__init__(d, rootfs_dir)
263
264 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
265 self.opkg_args = "-f %s -o %s " % (config_file, rootfs_dir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500266 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500267
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500268 def list_pkgs(self, format=None):
269 cmd = "%s %s status" % (self.opkg_cmd, self.opkg_args)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500270
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500271 # opkg returns success even when it printed some
272 # "Collected errors:" report to stderr. Mixing stderr into
273 # stdout then leads to random failures later on when
274 # parsing the output. To avoid this we need to collect both
275 # output streams separately and check for empty stderr.
276 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
277 cmd_output, cmd_stderr = p.communicate()
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600278 cmd_output = cmd_output.decode("utf-8")
279 cmd_stderr = cmd_stderr.decode("utf-8")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500280 if p.returncode or cmd_stderr:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500281 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500282 "returned %d and stderr:\n%s" % (cmd, p.returncode, cmd_stderr))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600284 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500285
286
287class DpkgPkgsList(PkgsList):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500288
289 def list_pkgs(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500290 cmd = [bb.utils.which(os.getenv('PATH'), "dpkg-query"),
291 "--admindir=%s/var/lib/dpkg" % self.rootfs_dir,
292 "-W"]
293
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500294 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 -0500295
296 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600297 cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip().decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500298 except subprocess.CalledProcessError as e:
299 bb.fatal("Cannot get the installed packages list. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600300 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500301
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600302 return opkg_query(cmd_output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500303
304
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600305class PackageManager(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500306 """
307 This is an abstract class. Do not instantiate this directly.
308 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500309
310 def __init__(self, d):
311 self.d = d
312 self.deploy_dir = None
313 self.deploy_lock = None
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500314
315 """
316 Update the package manager package database.
317 """
318 @abstractmethod
319 def update(self):
320 pass
321
322 """
323 Install a list of packages. 'pkgs' is a list object. If 'attempt_only' is
324 True, installation failures are ignored.
325 """
326 @abstractmethod
327 def install(self, pkgs, attempt_only=False):
328 pass
329
330 """
331 Remove a list of packages. 'pkgs' is a list object. If 'with_dependencies'
332 is False, the any dependencies are left in place.
333 """
334 @abstractmethod
335 def remove(self, pkgs, with_dependencies=True):
336 pass
337
338 """
339 This function creates the index files
340 """
341 @abstractmethod
342 def write_index(self):
343 pass
344
345 @abstractmethod
346 def remove_packaging_data(self):
347 pass
348
349 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500350 def list_installed(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351 pass
352
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500353 """
354 Returns the path to a tmpdir where resides the contents of a package.
355
356 Deleting the tmpdir is responsability of the caller.
357
358 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500359 @abstractmethod
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500360 def extract(self, pkg):
361 pass
362
363 """
364 Add remote package feeds into repository manager configuration. The parameters
365 for the feeds are set by feed_uris, feed_base_paths and feed_archs.
366 See http://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#var-PACKAGE_FEED_URIS
367 for their description.
368 """
369 @abstractmethod
370 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500371 pass
372
373 """
374 Install complementary packages based upon the list of currently installed
375 packages e.g. locales, *-dev, *-dbg, etc. This will only attempt to install
376 these packages, if they don't exist then no error will occur. Note: every
377 backend needs to call this function explicitly after the normal package
378 installation
379 """
380 def install_complementary(self, globs=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500381 if globs is None:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500382 globs = self.d.getVar('IMAGE_INSTALL_COMPLEMENTARY')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500383 split_linguas = set()
384
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500385 for translation in self.d.getVar('IMAGE_LINGUAS').split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500386 split_linguas.add(translation)
387 split_linguas.add(translation.split('-')[0])
388
389 split_linguas = sorted(split_linguas)
390
391 for lang in split_linguas:
392 globs += " *-locale-%s" % lang
393
394 if globs is None:
395 return
396
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500397 # we need to write the list of installed packages to a file because the
398 # oe-pkgdata-util reads it from a file
399 with tempfile.NamedTemporaryFile(mode="w+", prefix="installed-pkgs") as installed_pkgs:
400 pkgs = self.list_installed()
401 output = oe.utils.format_pkg_list(pkgs, "arch")
402 installed_pkgs.write(output)
403 installed_pkgs.flush()
404
405 cmd = [bb.utils.which(os.getenv('PATH'), "oe-pkgdata-util"),
406 "-p", self.d.getVar('PKGDATA_DIR'), "glob", installed_pkgs.name,
407 globs]
408 exclude = self.d.getVar('PACKAGE_EXCLUDE_COMPLEMENTARY')
409 if exclude:
410 cmd.extend(['--exclude=' + '|'.join(exclude.split())])
411 try:
412 bb.note("Installing complementary packages ...")
413 bb.note('Running %s' % cmd)
414 complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8")
415 except subprocess.CalledProcessError as e:
416 bb.fatal("Could not compute complementary packages list. Command "
417 "'%s' returned %d:\n%s" %
418 (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
419 self.install(complementary_pkgs.split(), attempt_only=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500420
421 def deploy_dir_lock(self):
422 if self.deploy_dir is None:
423 raise RuntimeError("deploy_dir is not set!")
424
425 lock_file_name = os.path.join(self.deploy_dir, "deploy.lock")
426
427 self.deploy_lock = bb.utils.lockfile(lock_file_name)
428
429 def deploy_dir_unlock(self):
430 if self.deploy_lock is None:
431 return
432
433 bb.utils.unlockfile(self.deploy_lock)
434
435 self.deploy_lock = None
436
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500437 """
438 Construct URIs based on the following pattern: uri/base_path where 'uri'
439 and 'base_path' correspond to each element of the corresponding array
440 argument leading to len(uris) x len(base_paths) elements on the returned
441 array
442 """
443 def construct_uris(self, uris, base_paths):
444 def _append(arr1, arr2, sep='/'):
445 res = []
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600446 narr1 = [a.rstrip(sep) for a in arr1]
447 narr2 = [a.rstrip(sep).lstrip(sep) for a in arr2]
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500448 for a1 in narr1:
449 if arr2:
450 for a2 in narr2:
451 res.append("%s%s%s" % (a1, sep, a2))
452 else:
453 res.append(a1)
454 return res
455 return _append(uris, base_paths)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500456
457class RpmPM(PackageManager):
458 def __init__(self,
459 d,
460 target_rootfs,
461 target_vendor,
462 task_name='target',
463 providename=None,
464 arch_var=None,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500465 os_var=None,
466 rpm_repo_workdir="oe-rootfs-repo"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500467 super(RpmPM, self).__init__(d)
468 self.target_rootfs = target_rootfs
469 self.target_vendor = target_vendor
470 self.task_name = task_name
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500471 if arch_var == None:
472 self.archs = self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS').replace("-","_")
473 else:
474 self.archs = self.d.getVar(arch_var).replace("-","_")
475 if task_name == "host":
476 self.primary_arch = self.d.getVar('SDK_ARCH')
477 else:
478 self.primary_arch = self.d.getVar('MACHINE_ARCH')
479
480 self.rpm_repo_dir = oe.path.join(self.d.getVar('WORKDIR'), rpm_repo_workdir)
481 bb.utils.mkdirhier(self.rpm_repo_dir)
482 oe.path.symlink(self.d.getVar('DEPLOY_DIR_RPM'), oe.path.join(self.rpm_repo_dir, "rpm"), True)
483
484 self.saved_packaging_data = self.d.expand('${T}/saved_packaging_data/%s' % self.task_name)
485 if not os.path.exists(self.d.expand('${T}/saved_packaging_data')):
486 bb.utils.mkdirhier(self.d.expand('${T}/saved_packaging_data'))
487 self.packaging_data_dirs = ['var/lib/rpm', 'var/lib/dnf', 'var/cache/dnf']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500488 self.solution_manifest = self.d.expand('${T}/saved/%s_solution' %
489 self.task_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500490 if not os.path.exists(self.d.expand('${T}/saved')):
491 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
492
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500493 def _configure_dnf(self):
494 # libsolv handles 'noarch' internally, we don't need to specify it explicitly
495 archs = [i for i in reversed(self.archs.split()) if i not in ["any", "all", "noarch"]]
496 # This prevents accidental matching against libsolv's built-in policies
497 if len(archs) <= 1:
498 archs = archs + ["bogusarch"]
499 confdir = "%s/%s" %(self.target_rootfs, "etc/dnf/vars/")
500 bb.utils.mkdirhier(confdir)
501 open(confdir + "arch", 'w').write(":".join(archs))
502 distro_codename = self.d.getVar('DISTRO_CODENAME')
503 open(confdir + "releasever", 'w').write(distro_codename if distro_codename is not None else '')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500504
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500505 open(oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"), 'w').write("")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500506
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500507
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500508 def _configure_rpm(self):
509 # We need to configure rpm to use our primary package architecture as the installation architecture,
510 # and to make it compatible with other package architectures that we use.
511 # Otherwise it will refuse to proceed with packages installation.
512 platformconfdir = "%s/%s" %(self.target_rootfs, "etc/rpm/")
513 rpmrcconfdir = "%s/%s" %(self.target_rootfs, "etc/")
514 bb.utils.mkdirhier(platformconfdir)
515 open(platformconfdir + "platform", 'w').write("%s-pc-linux" % self.primary_arch)
516 open(rpmrcconfdir + "rpmrc", 'w').write("arch_compat: %s: %s\n" % (self.primary_arch, self.archs if len(self.archs) > 0 else self.primary_arch))
517
518 open(platformconfdir + "macros", 'w').write("%_transaction_color 7\n")
519 if self.d.getVar('RPM_PREFER_ELF_ARCH'):
520 open(platformconfdir + "macros", 'a').write("%%_prefer_color %s" % (self.d.getVar('RPM_PREFER_ELF_ARCH')))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500521 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500522 open(platformconfdir + "macros", 'a').write("%_prefer_color 7")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500523
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500524 if self.d.getVar('RPM_SIGN_PACKAGES') == '1':
525 signer = get_signer(self.d, self.d.getVar('RPM_GPG_BACKEND'))
526 pubkey_path = oe.path.join(self.d.getVar('B'), 'rpm-key')
527 signer.export_pubkey(pubkey_path, self.d.getVar('RPM_GPG_NAME'))
528 rpm_bin = bb.utils.which(os.getenv('PATH'), "rpmkeys")
529 cmd = [rpm_bin, '--root=%s' % self.target_rootfs, '--import', pubkey_path]
530 try:
531 subprocess.check_output(cmd, stderr=subprocess.STDOUT)
532 except subprocess.CalledProcessError as e:
533 bb.fatal("Importing GPG key failed. Command '%s' "
534 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500535
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500536 def create_configs(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500537 self._configure_dnf()
538 self._configure_rpm()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500539
540 def write_index(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500541 lockfilename = self.d.getVar('DEPLOY_DIR_RPM') + "/rpm.lock"
542 lf = bb.utils.lockfile(lockfilename, False)
543 RpmIndexer(self.d, self.rpm_repo_dir).write_index()
544 bb.utils.unlockfile(lf)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500545
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500546 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
547 from urllib.parse import urlparse
548
549 if feed_uris == "":
550 return
551
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500552 gpg_opts = ''
553 if self.d.getVar('PACKAGE_FEED_SIGN') == '1':
554 gpg_opts += 'repo_gpgcheck=1\n'
555 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'))
556
557 if self.d.getVar('RPM_SIGN_PACKAGES') == '0':
558 gpg_opts += 'gpgcheck=0\n'
559
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500560 bb.utils.mkdirhier(oe.path.join(self.target_rootfs, "etc", "yum.repos.d"))
561 remote_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
562 for uri in remote_uris:
563 repo_base = "oe-remote-repo" + "-".join(urlparse(uri).path.split("/"))
564 if feed_archs is not None:
565 for arch in feed_archs.split():
566 repo_uri = uri + "/" + arch
567 repo_id = "oe-remote-repo" + "-".join(urlparse(repo_uri).path.split("/"))
568 repo_name = "OE Remote Repo:" + " ".join(urlparse(repo_uri).path.split("/"))
569 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'a').write(
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500570 "[%s]\nname=%s\nbaseurl=%s\n%s\n" % (repo_id, repo_name, repo_uri, gpg_opts))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500571 else:
572 repo_name = "OE Remote Repo:" + " ".join(urlparse(uri).path.split("/"))
573 repo_uri = uri
574 open(oe.path.join(self.target_rootfs, "etc", "yum.repos.d", repo_base + ".repo"), 'w').write(
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500575 "[%s]\nname=%s\nbaseurl=%s\n%s" % (repo_base, repo_name, repo_uri, gpg_opts))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500576
577 def _prepare_pkg_transaction(self):
578 os.environ['D'] = self.target_rootfs
579 os.environ['OFFLINE_ROOT'] = self.target_rootfs
580 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
581 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
582 os.environ['INTERCEPT_DIR'] = oe.path.join(self.d.getVar('WORKDIR'),
583 "intercept_scripts")
584 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
585
586
587 def install(self, pkgs, attempt_only = False):
588 if len(pkgs) == 0:
589 return
590 self._prepare_pkg_transaction()
591
592 bad_recommendations = self.d.getVar('BAD_RECOMMENDATIONS')
593 package_exclude = self.d.getVar('PACKAGE_EXCLUDE')
594 exclude_pkgs = (bad_recommendations.split() if bad_recommendations else []) + (package_exclude.split() if package_exclude else [])
595
596 output = self._invoke_dnf((["--skip-broken"] if attempt_only else []) +
597 (["-x", ",".join(exclude_pkgs)] if len(exclude_pkgs) > 0 else []) +
598 (["--setopt=install_weak_deps=False"] if self.d.getVar('NO_RECOMMENDATIONS') == "1" else []) +
599 (["--nogpgcheck"] if self.d.getVar('RPM_SIGN_PACKAGES') != '1' else ["--setopt=gpgcheck=True"]) +
600 ["install"] +
601 pkgs)
602
603 failed_scriptlets_pkgnames = collections.OrderedDict()
604 for line in output.splitlines():
605 if line.startswith("Non-fatal POSTIN scriptlet failure in rpm package"):
606 failed_scriptlets_pkgnames[line.split()[-1]] = True
607
608 for pkg in failed_scriptlets_pkgnames.keys():
609 self.save_rpmpostinst(pkg)
610
611 def remove(self, pkgs, with_dependencies = True):
612 if len(pkgs) == 0:
613 return
614 self._prepare_pkg_transaction()
615
616 if with_dependencies:
617 self._invoke_dnf(["remove"] + pkgs)
618 else:
619 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500620 args = ["-e", "-v", "--nodeps", "--root=%s" %self.target_rootfs]
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500621
622 try:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500623 bb.note("Running %s" % ' '.join([cmd] + args + pkgs))
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500624 output = subprocess.check_output([cmd] + args + pkgs, stderr=subprocess.STDOUT).decode("utf-8")
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500625 bb.note(output)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500626 except subprocess.CalledProcessError as e:
627 bb.fatal("Could not invoke rpm. Command "
628 "'%s' returned %d:\n%s" % (' '.join([cmd] + args + pkgs), e.returncode, e.output.decode("utf-8")))
629
630 def upgrade(self):
631 self._prepare_pkg_transaction()
632 self._invoke_dnf(["upgrade"])
633
634 def autoremove(self):
635 self._prepare_pkg_transaction()
636 self._invoke_dnf(["autoremove"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500637
638 def remove_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500639 self._invoke_dnf(["clean", "all"])
640 for dir in self.packaging_data_dirs:
641 bb.utils.remove(oe.path.join(self.target_rootfs, dir), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500642
643 def backup_packaging_data(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500644 # Save the packaging dirs for increment rpm image generation
645 if os.path.exists(self.saved_packaging_data):
646 bb.utils.remove(self.saved_packaging_data, True)
647 for i in self.packaging_data_dirs:
648 source_dir = oe.path.join(self.target_rootfs, i)
649 target_dir = oe.path.join(self.saved_packaging_data, i)
650 shutil.copytree(source_dir, target_dir, symlinks=True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500651
652 def recovery_packaging_data(self):
653 # Move the rpmlib back
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500654 if os.path.exists(self.saved_packaging_data):
655 for i in self.packaging_data_dirs:
656 target_dir = oe.path.join(self.target_rootfs, i)
657 if os.path.exists(target_dir):
658 bb.utils.remove(target_dir, True)
659 source_dir = oe.path.join(self.saved_packaging_data, i)
660 shutil.copytree(source_dir,
661 target_dir,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500662 symlinks=True)
663
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500664 def list_installed(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500665 output = self._invoke_dnf(["repoquery", "--installed", "--queryformat", "Package: %{name} %{arch} %{version} %{name}-%{version}-%{release}.%{arch}.rpm\nDependencies:\n%{requires}\nRecommendations:\n%{recommends}\nDependenciesEndHere:\n"],
666 print_output = False)
667 packages = {}
668 current_package = None
669 current_deps = None
670 current_state = "initial"
671 for line in output.splitlines():
672 if line.startswith("Package:"):
673 package_info = line.split(" ")[1:]
674 current_package = package_info[0]
675 package_arch = package_info[1]
676 package_version = package_info[2]
677 package_rpm = package_info[3]
678 packages[current_package] = {"arch":package_arch, "ver":package_version, "filename":package_rpm}
679 current_deps = []
680 elif line.startswith("Dependencies:"):
681 current_state = "dependencies"
682 elif line.startswith("Recommendations"):
683 current_state = "recommendations"
684 elif line.startswith("DependenciesEndHere:"):
685 current_state = "initial"
686 packages[current_package]["deps"] = current_deps
687 elif len(line) > 0:
688 if current_state == "dependencies":
689 current_deps.append(line)
690 elif current_state == "recommendations":
691 current_deps.append("%s [REC]" % line)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500692
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500693 return packages
694
695 def update(self):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500696 self._invoke_dnf(["makecache", "--refresh"])
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500697
698 def _invoke_dnf(self, dnf_args, fatal = True, print_output = True ):
699 os.environ['RPM_ETCCONFIGDIR'] = self.target_rootfs
700
701 dnf_cmd = bb.utils.which(os.getenv('PATH'), "dnf")
702 standard_dnf_args = (["-v", "--rpmverbosity=debug"] if self.d.getVar('ROOTFS_RPM_DEBUG') else []) + ["-y",
703 "-c", oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"),
704 "--setopt=reposdir=%s" %(oe.path.join(self.target_rootfs, "etc/yum.repos.d")),
705 "--repofrompath=oe-repo,%s" % (self.rpm_repo_dir),
706 "--installroot=%s" % (self.target_rootfs),
707 "--setopt=logdir=%s" % (self.d.getVar('T'))
708 ]
709 cmd = [dnf_cmd] + standard_dnf_args + dnf_args
710 try:
711 output = subprocess.check_output(cmd,stderr=subprocess.STDOUT).decode("utf-8")
712 if print_output:
713 bb.note(output)
714 return output
715 except subprocess.CalledProcessError as e:
716 if print_output:
717 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
718 "'%s' returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8")))
719 else:
720 (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command "
721 "'%s' returned %d:" % (' '.join(cmd), e.returncode))
722 return e.output.decode("utf-8")
723
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500724 def dump_install_solution(self, pkgs):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500725 open(self.solution_manifest, 'w').write(" ".join(pkgs))
726 return pkgs
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500727
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500728 def load_old_install_solution(self):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500729 if not os.path.exists(self.solution_manifest):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500730 return []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500731
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500732 return open(self.solution_manifest, 'r').read().split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500733
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500734 def _script_num_prefix(self, path):
735 files = os.listdir(path)
736 numbers = set()
737 numbers.add(99)
738 for f in files:
739 numbers.add(int(f.split("-")[0]))
740 return max(numbers) + 1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500741
742 def save_rpmpostinst(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500743 bb.note("Saving postinstall script of %s" % (pkg))
744 cmd = bb.utils.which(os.getenv('PATH'), "rpm")
745 args = ["-q", "--root=%s" % self.target_rootfs, "--queryformat", "%{postin}", pkg]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500746
747 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500748 output = subprocess.check_output([cmd] + args,stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500749 except subprocess.CalledProcessError as e:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500750 bb.fatal("Could not invoke rpm. Command "
751 "'%s' returned %d:\n%s" % (' '.join([cmd] + args), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500752
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500753 # may need to prepend #!/bin/sh to output
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500754
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500755 target_path = oe.path.join(self.target_rootfs, self.d.expand('${sysconfdir}/rpm-postinsts/'))
756 bb.utils.mkdirhier(target_path)
757 num = self._script_num_prefix(target_path)
758 saved_script_name = oe.path.join(target_path, "%d-%s" % (num, pkg))
759 open(saved_script_name, 'w').write(output)
760 os.chmod(saved_script_name, 0o755)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500761
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600762 def extract(self, pkg):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500763 output = self._invoke_dnf(["repoquery", "--queryformat", "%{location}", pkg])
764 pkg_name = output.splitlines()[-1]
765 if not pkg_name.endswith(".rpm"):
766 bb.fatal("dnf could not find package %s in repository: %s" %(pkg, output))
767 pkg_path = oe.path.join(self.rpm_repo_dir, pkg_name)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600768
769 cpio_cmd = bb.utils.which(os.getenv("PATH"), "cpio")
770 rpm2cpio_cmd = bb.utils.which(os.getenv("PATH"), "rpm2cpio")
771
772 if not os.path.isfile(pkg_path):
773 bb.fatal("Unable to extract package for '%s'."
774 "File %s doesn't exists" % (pkg, pkg_path))
775
776 tmp_dir = tempfile.mkdtemp()
777 current_dir = os.getcwd()
778 os.chdir(tmp_dir)
779
780 try:
781 cmd = "%s %s | %s -idmv" % (rpm2cpio_cmd, pkg_path, cpio_cmd)
782 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
783 except subprocess.CalledProcessError as e:
784 bb.utils.remove(tmp_dir, recurse=True)
785 bb.fatal("Unable to extract %s package. Command '%s' "
786 "returned %d:\n%s" % (pkg_path, cmd, e.returncode, e.output.decode("utf-8")))
787 except OSError as e:
788 bb.utils.remove(tmp_dir, recurse=True)
789 bb.fatal("Unable to extract %s package. Command '%s' "
790 "returned %d:\n%s at %s" % (pkg_path, cmd, e.errno, e.strerror, e.filename))
791
792 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
793 os.chdir(current_dir)
794
795 return tmp_dir
796
797
798class OpkgDpkgPM(PackageManager):
799 """
800 This is an abstract class. Do not instantiate this directly.
801 """
802 def __init__(self, d):
803 super(OpkgDpkgPM, self).__init__(d)
804
805 """
806 Returns a dictionary with the package info.
807
808 This method extracts the common parts for Opkg and Dpkg
809 """
810 def package_info(self, pkg, cmd):
811
812 try:
813 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).decode("utf-8")
814 except subprocess.CalledProcessError as e:
815 bb.fatal("Unable to list available packages. Command '%s' "
816 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
817 return opkg_query(output)
818
819 """
820 Returns the path to a tmpdir where resides the contents of a package.
821
822 Deleting the tmpdir is responsability of the caller.
823
824 This method extracts the common parts for Opkg and Dpkg
825 """
826 def extract(self, pkg, pkg_info):
827
828 ar_cmd = bb.utils.which(os.getenv("PATH"), "ar")
829 tar_cmd = bb.utils.which(os.getenv("PATH"), "tar")
830 pkg_path = pkg_info[pkg]["filepath"]
831
832 if not os.path.isfile(pkg_path):
833 bb.fatal("Unable to extract package for '%s'."
834 "File %s doesn't exists" % (pkg, pkg_path))
835
836 tmp_dir = tempfile.mkdtemp()
837 current_dir = os.getcwd()
838 os.chdir(tmp_dir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500839 if self.d.getVar('IMAGE_PKGTYPE') == 'deb':
840 data_tar = 'data.tar.xz'
841 else:
842 data_tar = 'data.tar.gz'
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600843
844 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500845 cmd = [ar_cmd, 'x', pkg_path]
846 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
847 cmd = [tar_cmd, 'xf', data_tar]
848 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600849 except subprocess.CalledProcessError as e:
850 bb.utils.remove(tmp_dir, recurse=True)
851 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500852 "returned %d:\n%s" % (pkg_path, ' '.join(cmd), e.returncode, e.output.decode("utf-8")))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600853 except OSError as e:
854 bb.utils.remove(tmp_dir, recurse=True)
855 bb.fatal("Unable to extract %s package. Command '%s' "
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500856 "returned %d:\n%s at %s" % (pkg_path, ' '.join(cmd), e.errno, e.strerror, e.filename))
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600857
858 bb.note("Extracted %s to %s" % (pkg_path, tmp_dir))
859 bb.utils.remove(os.path.join(tmp_dir, "debian-binary"))
860 bb.utils.remove(os.path.join(tmp_dir, "control.tar.gz"))
861 os.chdir(current_dir)
862
863 return tmp_dir
864
865
866class OpkgPM(OpkgDpkgPM):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500867 def __init__(self, d, target_rootfs, config_file, archs, task_name='target'):
868 super(OpkgPM, self).__init__(d)
869
870 self.target_rootfs = target_rootfs
871 self.config_file = config_file
872 self.pkg_archs = archs
873 self.task_name = task_name
874
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500875 self.deploy_dir = self.d.getVar("DEPLOY_DIR_IPK")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500876 self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock")
877 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600878 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 -0500879 self.opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500880
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500881 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500882 if opkg_lib_dir[0] == "/":
883 opkg_lib_dir = opkg_lib_dir[1:]
884
885 self.opkg_dir = os.path.join(target_rootfs, opkg_lib_dir, "opkg")
886
887 bb.utils.mkdirhier(self.opkg_dir)
888
889 self.saved_opkg_dir = self.d.expand('${T}/saved/%s' % self.task_name)
890 if not os.path.exists(self.d.expand('${T}/saved')):
891 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
892
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500893 self.from_feeds = (self.d.getVar('BUILD_IMAGES_FROM_FEEDS') or "") == "1"
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500894 if self.from_feeds:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500895 self._create_custom_config()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500896 else:
897 self._create_config()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500898
899 self.indexer = OpkgIndexer(self.d, self.deploy_dir)
900
901 """
902 This function will change a package's status in /var/lib/opkg/status file.
903 If 'packages' is None then the new_status will be applied to all
904 packages
905 """
906 def mark_packages(self, status_tag, packages=None):
907 status_file = os.path.join(self.opkg_dir, "status")
908
909 with open(status_file, "r") as sf:
910 with open(status_file + ".tmp", "w+") as tmp_sf:
911 if packages is None:
912 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
913 r"Package: \1\n\2Status: \3%s" % status_tag,
914 sf.read()))
915 else:
916 if type(packages).__name__ != "list":
917 raise TypeError("'packages' should be a list object")
918
919 status = sf.read()
920 for pkg in packages:
921 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
922 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
923 status)
924
925 tmp_sf.write(status)
926
927 os.rename(status_file + ".tmp", status_file)
928
929 def _create_custom_config(self):
930 bb.note("Building from feeds activated!")
931
932 with open(self.config_file, "w+") as config_file:
933 priority = 1
934 for arch in self.pkg_archs.split():
935 config_file.write("arch %s %d\n" % (arch, priority))
936 priority += 5
937
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500938 for line in (self.d.getVar('IPK_FEED_URIS') or "").split():
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500939 feed_match = re.match("^[ \t]*(.*)##([^ \t]*)[ \t]*$", line)
940
941 if feed_match is not None:
942 feed_name = feed_match.group(1)
943 feed_uri = feed_match.group(2)
944
945 bb.note("Add %s feed with URL %s" % (feed_name, feed_uri))
946
947 config_file.write("src/gz %s %s\n" % (feed_name, feed_uri))
948
949 """
950 Allow to use package deploy directory contents as quick devel-testing
951 feed. This creates individual feed configs for each arch subdir of those
952 specified as compatible for the current machine.
953 NOTE: Development-helper feature, NOT a full-fledged feed.
954 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500955 if (self.d.getVar('FEED_DEPLOYDIR_BASE_URI') or "") != "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500956 for arch in self.pkg_archs.split():
957 cfg_file_name = os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500958 self.d.getVar("sysconfdir"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500959 "opkg",
960 "local-%s-feed.conf" % arch)
961
962 with open(cfg_file_name, "w+") as cfg_file:
963 cfg_file.write("src/gz local-%s %s/%s" %
964 (arch,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500965 self.d.getVar('FEED_DEPLOYDIR_BASE_URI'),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500966 arch))
967
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500968 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500969 # There is no command line option for this anymore, we need to add
970 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
971 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500972 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
973 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
974 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500975 cfg_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
976 cfg_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
977 cfg_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500978
979
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500980 def _create_config(self):
981 with open(self.config_file, "w+") as config_file:
982 priority = 1
983 for arch in self.pkg_archs.split():
984 config_file.write("arch %s %d\n" % (arch, priority))
985 priority += 5
986
987 config_file.write("src oe file:%s\n" % self.deploy_dir)
988
989 for arch in self.pkg_archs.split():
990 pkgs_dir = os.path.join(self.deploy_dir, arch)
991 if os.path.isdir(pkgs_dir):
992 config_file.write("src oe-%s file:%s\n" %
993 (arch, pkgs_dir))
994
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500995 if self.d.getVar('OPKGLIBDIR') != '/var/lib':
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500996 # There is no command line option for this anymore, we need to add
997 # info_dir and status_file to config file, if OPKGLIBDIR doesn't have
998 # the default value of "/var/lib" as defined in opkg:
Brad Bishop37a0e4d2017-12-04 01:01:44 -0500999 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_LISTS_DIR VARDIR "/lib/opkg/lists"
1000 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_INFO_DIR VARDIR "/lib/opkg/info"
1001 # libopkg/opkg_conf.h:#define OPKG_CONF_DEFAULT_STATUS_FILE VARDIR "/lib/opkg/status"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001002 config_file.write("option info_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'info'))
1003 config_file.write("option lists_dir %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'lists'))
1004 config_file.write("option status_file %s\n" % os.path.join(self.d.getVar('OPKGLIBDIR'), 'opkg', 'status'))
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001005
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001006 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1007 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001008 return
1009
1010 rootfs_config = os.path.join('%s/etc/opkg/base-feeds.conf'
1011 % self.target_rootfs)
1012
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001013 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
1014 archs = self.pkg_archs.split() if feed_archs is None else feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001015
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001016 with open(rootfs_config, "w+") as config_file:
1017 uri_iterator = 0
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001018 for uri in feed_uris:
1019 if archs:
1020 for arch in archs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001021 if (feed_archs is None) and (not os.path.exists(oe.path.join(self.deploy_dir, arch))):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001022 continue
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001023 bb.note('Adding opkg feed url-%s-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001024 (arch, uri_iterator, uri))
1025 config_file.write("src/gz uri-%s-%d %s/%s\n" %
1026 (arch, uri_iterator, uri, arch))
1027 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001028 bb.note('Adding opkg feed url-%d (%s)' %
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001029 (uri_iterator, uri))
1030 config_file.write("src/gz uri-%d %s\n" %
1031 (uri_iterator, uri))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001032
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001033 uri_iterator += 1
1034
1035 def update(self):
1036 self.deploy_dir_lock()
1037
1038 cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args)
1039
1040 try:
1041 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1042 except subprocess.CalledProcessError as e:
1043 self.deploy_dir_unlock()
1044 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001045 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001046
1047 self.deploy_dir_unlock()
1048
1049 def install(self, pkgs, attempt_only=False):
Patrick Williamsf1e5d692016-03-30 15:21:19 -05001050 if not pkgs:
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001051 return
1052
1053 cmd = "%s %s install %s" % (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1054
1055 os.environ['D'] = self.target_rootfs
1056 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1057 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1058 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001059 os.environ['INTERCEPT_DIR'] = os.path.join(self.d.getVar('WORKDIR'),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001060 "intercept_scripts")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001061 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001062
1063 try:
1064 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1065 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001066 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001067 bb.note(output)
1068 except subprocess.CalledProcessError as e:
1069 (bb.fatal, bb.note)[attempt_only]("Unable to install packages. "
1070 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001071 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001072
1073 def remove(self, pkgs, with_dependencies=True):
1074 if with_dependencies:
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001075 cmd = "%s %s --force-remove --force-removal-of-dependent-packages remove %s" % \
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001076 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1077 else:
1078 cmd = "%s %s --force-depends remove %s" % \
1079 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1080
1081 try:
1082 bb.note(cmd)
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001083 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT).decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001084 bb.note(output)
1085 except subprocess.CalledProcessError as e:
1086 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001087 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001088
1089 def write_index(self):
1090 self.deploy_dir_lock()
1091
1092 result = self.indexer.write_index()
1093
1094 self.deploy_dir_unlock()
1095
1096 if result is not None:
1097 bb.fatal(result)
1098
1099 def remove_packaging_data(self):
1100 bb.utils.remove(self.opkg_dir, True)
1101 # create the directory back, it's needed by PM lock
1102 bb.utils.mkdirhier(self.opkg_dir)
1103
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001104 def remove_lists(self):
1105 if not self.from_feeds:
1106 bb.utils.remove(os.path.join(self.opkg_dir, "lists"), True)
1107
1108 def list_installed(self):
1109 return OpkgPkgsList(self.d, self.target_rootfs, self.config_file).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001110
1111 def handle_bad_recommendations(self):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001112 bad_recommendations = self.d.getVar("BAD_RECOMMENDATIONS") or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001113 if bad_recommendations.strip() == "":
1114 return
1115
1116 status_file = os.path.join(self.opkg_dir, "status")
1117
1118 # If status file existed, it means the bad recommendations has already
1119 # been handled
1120 if os.path.exists(status_file):
1121 return
1122
1123 cmd = "%s %s info " % (self.opkg_cmd, self.opkg_args)
1124
1125 with open(status_file, "w+") as status:
1126 for pkg in bad_recommendations.split():
1127 pkg_info = cmd + pkg
1128
1129 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001130 output = subprocess.check_output(pkg_info.split(), stderr=subprocess.STDOUT).strip().decode("utf-8")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001131 except subprocess.CalledProcessError as e:
1132 bb.fatal("Cannot get package info. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001133 "returned %d:\n%s" % (pkg_info, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001134
1135 if output == "":
1136 bb.note("Ignored bad recommendation: '%s' is "
1137 "not a package" % pkg)
1138 continue
1139
1140 for line in output.split('\n'):
1141 if line.startswith("Status:"):
1142 status.write("Status: deinstall hold not-installed\n")
1143 else:
1144 status.write(line + "\n")
1145
1146 # Append a blank line after each package entry to ensure that it
1147 # is separated from the following entry
1148 status.write("\n")
1149
1150 '''
1151 The following function dummy installs pkgs and returns the log of output.
1152 '''
1153 def dummy_install(self, pkgs):
1154 if len(pkgs) == 0:
1155 return
1156
1157 # Create an temp dir as opkg root for dummy installation
1158 temp_rootfs = self.d.expand('${T}/opkg')
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001159 opkg_lib_dir = self.d.getVar('OPKGLIBDIR')
Brad Bishop37a0e4d2017-12-04 01:01:44 -05001160 if opkg_lib_dir[0] == "/":
1161 opkg_lib_dir = opkg_lib_dir[1:]
1162 temp_opkg_dir = os.path.join(temp_rootfs, opkg_lib_dir, 'opkg')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001163 bb.utils.mkdirhier(temp_opkg_dir)
1164
1165 opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001166 opkg_args += self.d.getVar("OPKG_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001167
1168 cmd = "%s %s update" % (self.opkg_cmd, opkg_args)
1169 try:
1170 subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1171 except subprocess.CalledProcessError as e:
1172 bb.fatal("Unable to update. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001173 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001174
1175 # Dummy installation
1176 cmd = "%s %s --noaction install %s " % (self.opkg_cmd,
1177 opkg_args,
1178 ' '.join(pkgs))
1179 try:
1180 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1181 except subprocess.CalledProcessError as e:
1182 bb.fatal("Unable to dummy install packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001183 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001184
1185 bb.utils.remove(temp_rootfs, True)
1186
1187 return output
1188
1189 def backup_packaging_data(self):
1190 # Save the opkglib for increment ipk image generation
1191 if os.path.exists(self.saved_opkg_dir):
1192 bb.utils.remove(self.saved_opkg_dir, True)
1193 shutil.copytree(self.opkg_dir,
1194 self.saved_opkg_dir,
1195 symlinks=True)
1196
1197 def recover_packaging_data(self):
1198 # Move the opkglib back
1199 if os.path.exists(self.saved_opkg_dir):
1200 if os.path.exists(self.opkg_dir):
1201 bb.utils.remove(self.opkg_dir, True)
1202
1203 bb.note('Recover packaging data')
1204 shutil.copytree(self.saved_opkg_dir,
1205 self.opkg_dir,
1206 symlinks=True)
1207
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001208 """
1209 Returns a dictionary with the package info.
1210 """
1211 def package_info(self, pkg):
1212 cmd = "%s %s info %s" % (self.opkg_cmd, self.opkg_args, pkg)
1213 pkg_info = super(OpkgPM, self).package_info(pkg, cmd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001214
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001215 pkg_arch = pkg_info[pkg]["arch"]
1216 pkg_filename = pkg_info[pkg]["filename"]
1217 pkg_info[pkg]["filepath"] = \
1218 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1219
1220 return pkg_info
1221
1222 """
1223 Returns the path to a tmpdir where resides the contents of a package.
1224
1225 Deleting the tmpdir is responsability of the caller.
1226 """
1227 def extract(self, pkg):
1228 pkg_info = self.package_info(pkg)
1229 if not pkg_info:
1230 bb.fatal("Unable to get information for package '%s' while "
1231 "trying to extract the package." % pkg)
1232
1233 tmp_dir = super(OpkgPM, self).extract(pkg, pkg_info)
1234 bb.utils.remove(os.path.join(tmp_dir, "data.tar.gz"))
1235
1236 return tmp_dir
1237
1238class DpkgPM(OpkgDpkgPM):
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001239 def __init__(self, d, target_rootfs, archs, base_archs, apt_conf_dir=None):
1240 super(DpkgPM, self).__init__(d)
1241 self.target_rootfs = target_rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001242 self.deploy_dir = self.d.getVar('DEPLOY_DIR_DEB')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001243 if apt_conf_dir is None:
1244 self.apt_conf_dir = self.d.expand("${APTCONF_TARGET}/apt")
1245 else:
1246 self.apt_conf_dir = apt_conf_dir
1247 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
1248 self.apt_get_cmd = bb.utils.which(os.getenv('PATH'), "apt-get")
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001249 self.apt_cache_cmd = bb.utils.which(os.getenv('PATH'), "apt-cache")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001250
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001251 self.apt_args = d.getVar("APT_ARGS")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001252
1253 self.all_arch_list = archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001254 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001255 self.all_arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in self.all_arch_list)
1256
1257 self._create_configs(archs, base_archs)
1258
1259 self.indexer = DpkgIndexer(self.d, self.deploy_dir)
1260
1261 """
1262 This function will change a package's status in /var/lib/dpkg/status file.
1263 If 'packages' is None then the new_status will be applied to all
1264 packages
1265 """
1266 def mark_packages(self, status_tag, packages=None):
1267 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1268
1269 with open(status_file, "r") as sf:
1270 with open(status_file + ".tmp", "w+") as tmp_sf:
1271 if packages is None:
1272 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1273 r"Package: \1\n\2Status: \3%s" % status_tag,
1274 sf.read()))
1275 else:
1276 if type(packages).__name__ != "list":
1277 raise TypeError("'packages' should be a list object")
1278
1279 status = sf.read()
1280 for pkg in packages:
1281 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1282 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1283 status)
1284
1285 tmp_sf.write(status)
1286
1287 os.rename(status_file + ".tmp", status_file)
1288
1289 """
1290 Run the pre/post installs for package "package_name". If package_name is
1291 None, then run all pre/post install scriptlets.
1292 """
1293 def run_pre_post_installs(self, package_name=None):
1294 info_dir = self.target_rootfs + "/var/lib/dpkg/info"
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001295 ControlScript = collections.namedtuple("ControlScript", ["suffix", "name", "argument"])
1296 control_scripts = [
1297 ControlScript(".preinst", "Preinstall", "install"),
1298 ControlScript(".postinst", "Postinstall", "configure")]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001299 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1300 installed_pkgs = []
1301
1302 with open(status_file, "r") as status:
1303 for line in status.read().split('\n'):
1304 m = re.match("^Package: (.*)", line)
1305 if m is not None:
1306 installed_pkgs.append(m.group(1))
1307
1308 if package_name is not None and not package_name in installed_pkgs:
1309 return
1310
1311 os.environ['D'] = self.target_rootfs
1312 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1313 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1314 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001315 os.environ['INTERCEPT_DIR'] = os.path.join(self.d.getVar('WORKDIR'),
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001316 "intercept_scripts")
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001317 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001318
1319 failed_pkgs = []
1320 for pkg_name in installed_pkgs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001321 for control_script in control_scripts:
1322 p_full = os.path.join(info_dir, pkg_name + control_script.suffix)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001323 if os.path.exists(p_full):
1324 try:
1325 bb.note("Executing %s for package: %s ..." %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001326 (control_script.name.lower(), pkg_name))
1327 output = subprocess.check_output([p_full, control_script.argument],
1328 stderr=subprocess.STDOUT).decode("utf-8")
1329 bb.note(output)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001330 except subprocess.CalledProcessError as e:
1331 bb.note("%s for package %s failed with %d:\n%s" %
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001332 (control_script.name, pkg_name, e.returncode,
1333 e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001334 failed_pkgs.append(pkg_name)
1335 break
1336
1337 if len(failed_pkgs):
1338 self.mark_packages("unpacked", failed_pkgs)
1339
1340 def update(self):
1341 os.environ['APT_CONFIG'] = self.apt_conf_file
1342
1343 self.deploy_dir_lock()
1344
1345 cmd = "%s update" % self.apt_get_cmd
1346
1347 try:
1348 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1349 except subprocess.CalledProcessError as e:
1350 bb.fatal("Unable to update the package index files. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001351 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001352
1353 self.deploy_dir_unlock()
1354
1355 def install(self, pkgs, attempt_only=False):
1356 if attempt_only and len(pkgs) == 0:
1357 return
1358
1359 os.environ['APT_CONFIG'] = self.apt_conf_file
1360
1361 cmd = "%s %s install --force-yes --allow-unauthenticated %s" % \
1362 (self.apt_get_cmd, self.apt_args, ' '.join(pkgs))
1363
1364 try:
1365 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1366 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1367 except subprocess.CalledProcessError as e:
1368 (bb.fatal, bb.note)[attempt_only]("Unable to install packages. "
1369 "Command '%s' returned %d:\n%s" %
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001370 (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001371
1372 # rename *.dpkg-new files/dirs
1373 for root, dirs, files in os.walk(self.target_rootfs):
1374 for dir in dirs:
1375 new_dir = re.sub("\.dpkg-new", "", dir)
1376 if dir != new_dir:
1377 os.rename(os.path.join(root, dir),
1378 os.path.join(root, new_dir))
1379
1380 for file in files:
1381 new_file = re.sub("\.dpkg-new", "", file)
1382 if file != new_file:
1383 os.rename(os.path.join(root, file),
1384 os.path.join(root, new_file))
1385
1386
1387 def remove(self, pkgs, with_dependencies=True):
1388 if with_dependencies:
1389 os.environ['APT_CONFIG'] = self.apt_conf_file
1390 cmd = "%s purge %s" % (self.apt_get_cmd, ' '.join(pkgs))
1391 else:
1392 cmd = "%s --admindir=%s/var/lib/dpkg --instdir=%s" \
1393 " -P --force-depends %s" % \
1394 (bb.utils.which(os.getenv('PATH'), "dpkg"),
1395 self.target_rootfs, self.target_rootfs, ' '.join(pkgs))
1396
1397 try:
1398 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1399 except subprocess.CalledProcessError as e:
1400 bb.fatal("Unable to remove packages. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001401 "returned %d:\n%s" % (e.cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001402
1403 def write_index(self):
1404 self.deploy_dir_lock()
1405
1406 result = self.indexer.write_index()
1407
1408 self.deploy_dir_unlock()
1409
1410 if result is not None:
1411 bb.fatal(result)
1412
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001413 def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs):
1414 if feed_uris == "":
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001415 return
1416
1417 sources_conf = os.path.join("%s/etc/apt/sources.list"
1418 % self.target_rootfs)
1419 arch_list = []
1420
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001421 if feed_archs is None:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001422 for arch in self.all_arch_list:
1423 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1424 continue
1425 arch_list.append(arch)
1426 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001427 arch_list = feed_archs.split()
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001428
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001429 feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split())
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001430
1431 with open(sources_conf, "w+") as sources_file:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001432 for uri in feed_uris:
1433 if arch_list:
1434 for arch in arch_list:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001435 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001436 sources_file.write("deb %s/%s ./\n" %
1437 (uri, arch))
1438 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001439 bb.note('Adding dpkg channel at (%s)' % uri)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001440 sources_file.write("deb %s ./\n" % uri)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001441
1442 def _create_configs(self, archs, base_archs):
1443 base_archs = re.sub("_", "-", base_archs)
1444
1445 if os.path.exists(self.apt_conf_dir):
1446 bb.utils.remove(self.apt_conf_dir, True)
1447
1448 bb.utils.mkdirhier(self.apt_conf_dir)
1449 bb.utils.mkdirhier(self.apt_conf_dir + "/lists/partial/")
1450 bb.utils.mkdirhier(self.apt_conf_dir + "/apt.conf.d/")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001451 bb.utils.mkdirhier(self.apt_conf_dir + "/preferences.d/")
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001452
1453 arch_list = []
1454 for arch in self.all_arch_list:
1455 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1456 continue
1457 arch_list.append(arch)
1458
1459 with open(os.path.join(self.apt_conf_dir, "preferences"), "w+") as prefs_file:
1460 priority = 801
1461 for arch in arch_list:
1462 prefs_file.write(
1463 "Package: *\n"
1464 "Pin: release l=%s\n"
1465 "Pin-Priority: %d\n\n" % (arch, priority))
1466
1467 priority += 5
1468
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001469 pkg_exclude = self.d.getVar('PACKAGE_EXCLUDE') or ""
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001470 for pkg in pkg_exclude.split():
1471 prefs_file.write(
1472 "Package: %s\n"
1473 "Pin: release *\n"
1474 "Pin-Priority: -1\n\n" % pkg)
1475
1476 arch_list.reverse()
1477
1478 with open(os.path.join(self.apt_conf_dir, "sources.list"), "w+") as sources_file:
1479 for arch in arch_list:
1480 sources_file.write("deb file:%s/ ./\n" %
1481 os.path.join(self.deploy_dir, arch))
1482
1483 base_arch_list = base_archs.split()
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001484 multilib_variants = self.d.getVar("MULTILIB_VARIANTS");
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001485 for variant in multilib_variants.split():
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001486 localdata = bb.data.createCopy(self.d)
1487 variant_tune = localdata.getVar("DEFAULTTUNE_virtclass-multilib-" + variant, False)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001488 orig_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001489 localdata.setVar("DEFAULTTUNE", variant_tune)
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001490 variant_arch = localdata.getVar("DPKG_ARCH")
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001491 if variant_arch not in base_arch_list:
1492 base_arch_list.append(variant_arch)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001493
1494 with open(self.apt_conf_file, "w+") as apt_conf:
1495 with open(self.d.expand("${STAGING_ETCDIR_NATIVE}/apt/apt.conf.sample")) as apt_conf_sample:
1496 for line in apt_conf_sample.read().split("\n"):
1497 match_arch = re.match(" Architecture \".*\";$", line)
1498 architectures = ""
1499 if match_arch:
1500 for base_arch in base_arch_list:
1501 architectures += "\"%s\";" % base_arch
1502 apt_conf.write(" Architectures {%s};\n" % architectures);
1503 apt_conf.write(" Architecture \"%s\";\n" % base_archs)
1504 else:
1505 line = re.sub("#ROOTFS#", self.target_rootfs, line)
1506 line = re.sub("#APTCONF#", self.apt_conf_dir, line)
1507 apt_conf.write(line + "\n")
1508
1509 target_dpkg_dir = "%s/var/lib/dpkg" % self.target_rootfs
1510 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "info"))
1511
1512 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "updates"))
1513
1514 if not os.path.exists(os.path.join(target_dpkg_dir, "status")):
1515 open(os.path.join(target_dpkg_dir, "status"), "w+").close()
1516 if not os.path.exists(os.path.join(target_dpkg_dir, "available")):
1517 open(os.path.join(target_dpkg_dir, "available"), "w+").close()
1518
1519 def remove_packaging_data(self):
1520 bb.utils.remove(os.path.join(self.target_rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001521 self.d.getVar('opkglibdir')), True)
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001522 bb.utils.remove(self.target_rootfs + "/var/lib/dpkg/", True)
1523
1524 def fix_broken_dependencies(self):
1525 os.environ['APT_CONFIG'] = self.apt_conf_file
1526
1527 cmd = "%s %s -f install" % (self.apt_get_cmd, self.apt_args)
1528
1529 try:
1530 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1531 except subprocess.CalledProcessError as e:
1532 bb.fatal("Cannot fix broken dependencies. Command '%s' "
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001533 "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001534
Patrick Williamsd8c66bc2016-06-20 12:57:21 -05001535 def list_installed(self):
1536 return DpkgPkgsList(self.d, self.target_rootfs).list_pkgs()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001537
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001538 """
1539 Returns a dictionary with the package info.
1540 """
1541 def package_info(self, pkg):
1542 cmd = "%s show %s" % (self.apt_cache_cmd, pkg)
1543 pkg_info = super(DpkgPM, self).package_info(pkg, cmd)
1544
1545 pkg_arch = pkg_info[pkg]["pkgarch"]
1546 pkg_filename = pkg_info[pkg]["filename"]
1547 pkg_info[pkg]["filepath"] = \
1548 os.path.join(self.deploy_dir, pkg_arch, pkg_filename)
1549
1550 return pkg_info
1551
1552 """
1553 Returns the path to a tmpdir where resides the contents of a package.
1554
1555 Deleting the tmpdir is responsability of the caller.
1556 """
1557 def extract(self, pkg):
1558 pkg_info = self.package_info(pkg)
1559 if not pkg_info:
1560 bb.fatal("Unable to get information for package '%s' while "
1561 "trying to extract the package." % pkg)
1562
1563 tmp_dir = super(DpkgPM, self).extract(pkg, pkg_info)
1564 bb.utils.remove(os.path.join(tmp_dir, "data.tar.xz"))
1565
1566 return tmp_dir
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001567
1568def generate_index_files(d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001569 classes = d.getVar('PACKAGE_CLASSES').replace("package_", "").split()
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001570
1571 indexer_map = {
Brad Bishop6e60e8b2018-02-01 10:27:11 -05001572 "rpm": (RpmIndexer, d.getVar('DEPLOY_DIR_RPM')),
1573 "ipk": (OpkgIndexer, d.getVar('DEPLOY_DIR_IPK')),
1574 "deb": (DpkgIndexer, d.getVar('DEPLOY_DIR_DEB'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001575 }
1576
1577 result = None
1578
1579 for pkg_class in classes:
1580 if not pkg_class in indexer_map:
1581 continue
1582
1583 if os.path.exists(indexer_map[pkg_class][1]):
1584 result = indexer_map[pkg_class][0](d, indexer_map[pkg_class][1]).write_index()
1585
1586 if result is not None:
1587 bb.fatal(result)
1588
1589if __name__ == "__main__":
1590 """
1591 We should be able to run this as a standalone script, from outside bitbake
1592 environment.
1593 """
1594 """
1595 TBD
1596 """