blob: 292ed444617661ca41233e946d6d4c886ced1ad1 [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
8import bb
9import tempfile
10import oe.utils
11
12
13# this can be used by all PM backends to create the index files in parallel
14def create_index(arg):
15 index_cmd = arg
16
17 try:
18 bb.note("Executing '%s' ..." % index_cmd)
19 result = subprocess.check_output(index_cmd, stderr=subprocess.STDOUT, shell=True)
20 except subprocess.CalledProcessError as e:
21 return("Index creation command '%s' failed with return code %d:\n%s" %
22 (e.cmd, e.returncode, e.output))
23
24 if result:
25 bb.note(result)
26
27 return None
28
29
30class Indexer(object):
31 __metaclass__ = ABCMeta
32
33 def __init__(self, d, deploy_dir):
34 self.d = d
35 self.deploy_dir = deploy_dir
36
37 @abstractmethod
38 def write_index(self):
39 pass
40
41
42class RpmIndexer(Indexer):
43 def get_ml_prefix_and_os_list(self, arch_var=None, os_var=None):
44 package_archs = {
45 'default': [],
46 }
47
48 target_os = {
49 'default': "",
50 }
51
52 if arch_var is not None and os_var is not None:
53 package_archs['default'] = self.d.getVar(arch_var, True).split()
54 package_archs['default'].reverse()
55 target_os['default'] = self.d.getVar(os_var, True).strip()
56 else:
57 package_archs['default'] = self.d.getVar("PACKAGE_ARCHS", True).split()
58 # arch order is reversed. This ensures the -best- match is
59 # listed first!
60 package_archs['default'].reverse()
61 target_os['default'] = self.d.getVar("TARGET_OS", True).strip()
62 multilibs = self.d.getVar('MULTILIBS', True) or ""
63 for ext in multilibs.split():
64 eext = ext.split(':')
65 if len(eext) > 1 and eext[0] == 'multilib':
66 localdata = bb.data.createCopy(self.d)
67 default_tune_key = "DEFAULTTUNE_virtclass-multilib-" + eext[1]
68 default_tune = localdata.getVar(default_tune_key, False)
69 if default_tune is None:
70 default_tune_key = "DEFAULTTUNE_ML_" + eext[1]
71 default_tune = localdata.getVar(default_tune_key, False)
72 if default_tune:
73 localdata.setVar("DEFAULTTUNE", default_tune)
74 bb.data.update_data(localdata)
75 package_archs[eext[1]] = localdata.getVar('PACKAGE_ARCHS',
76 True).split()
77 package_archs[eext[1]].reverse()
78 target_os[eext[1]] = localdata.getVar("TARGET_OS",
79 True).strip()
80
81 ml_prefix_list = dict()
82 for mlib in package_archs:
83 if mlib == 'default':
84 ml_prefix_list[mlib] = package_archs[mlib]
85 else:
86 ml_prefix_list[mlib] = list()
87 for arch in package_archs[mlib]:
88 if arch in ['all', 'noarch', 'any']:
89 ml_prefix_list[mlib].append(arch)
90 else:
91 ml_prefix_list[mlib].append(mlib + "_" + arch)
92
93 return (ml_prefix_list, target_os)
94
95 def write_index(self):
96 sdk_pkg_archs = (self.d.getVar('SDK_PACKAGE_ARCHS', True) or "").replace('-', '_').split()
97 all_mlb_pkg_archs = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS', True) or "").replace('-', '_').split()
98
99 mlb_prefix_list = self.get_ml_prefix_and_os_list()[0]
100
101 archs = set()
102 for item in mlb_prefix_list:
103 archs = archs.union(set(i.replace('-', '_') for i in mlb_prefix_list[item]))
104
105 if len(archs) == 0:
106 archs = archs.union(set(all_mlb_pkg_archs))
107
108 archs = archs.union(set(sdk_pkg_archs))
109
110 rpm_createrepo = bb.utils.which(os.getenv('PATH'), "createrepo")
111 if self.d.getVar('PACKAGE_FEED_SIGN', True) == '1':
112 pkgfeed_gpg_name = self.d.getVar('PACKAGE_FEED_GPG_NAME', True)
113 pkgfeed_gpg_pass = self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE', True)
114 else:
115 pkgfeed_gpg_name = None
116 pkgfeed_gpg_pass = None
117 gpg_bin = self.d.getVar('GPG_BIN', True) or \
118 bb.utils.which(os.getenv('PATH'), "gpg")
119
120 index_cmds = []
121 repo_sign_cmds = []
122 rpm_dirs_found = False
123 for arch in archs:
124 dbpath = os.path.join(self.d.getVar('WORKDIR', True), 'rpmdb', arch)
125 if os.path.exists(dbpath):
126 bb.utils.remove(dbpath, True)
127 arch_dir = os.path.join(self.deploy_dir, arch)
128 if not os.path.isdir(arch_dir):
129 continue
130
131 index_cmds.append("%s --dbpath %s --update -q %s" % \
132 (rpm_createrepo, dbpath, arch_dir))
133 if pkgfeed_gpg_name:
134 repomd_file = os.path.join(arch_dir, 'repodata', 'repomd.xml')
135 gpg_cmd = "%s --detach-sign --armor --batch --no-tty --yes " \
136 "--passphrase-file '%s' -u '%s' %s" % (gpg_bin,
137 pkgfeed_gpg_pass, pkgfeed_gpg_name, repomd_file)
138 repo_sign_cmds.append(gpg_cmd)
139
140 rpm_dirs_found = True
141
142 if not rpm_dirs_found:
143 bb.note("There are no packages in %s" % self.deploy_dir)
144 return
145
146 # Create repodata
147 result = oe.utils.multiprocess_exec(index_cmds, create_index)
148 if result:
149 bb.fatal('%s' % ('\n'.join(result)))
150 # Sign repomd
151 result = oe.utils.multiprocess_exec(repo_sign_cmds, create_index)
152 if result:
153 bb.fatal('%s' % ('\n'.join(result)))
154 # Copy pubkey(s) to repo
155 distro_version = self.d.getVar('DISTRO_VERSION', True) or "oe.0"
156 if self.d.getVar('RPM_SIGN_PACKAGES', True) == '1':
157 shutil.copy2(self.d.getVar('RPM_GPG_PUBKEY', True),
158 os.path.join(self.deploy_dir,
159 'RPM-GPG-KEY-%s' % distro_version))
160 if self.d.getVar('PACKAGE_FEED_SIGN', True) == '1':
161 shutil.copy2(self.d.getVar('PACKAGE_FEED_GPG_PUBKEY', True),
162 os.path.join(self.deploy_dir,
163 'REPODATA-GPG-KEY-%s' % distro_version))
164
165
166class OpkgIndexer(Indexer):
167 def write_index(self):
168 arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS",
169 "SDK_PACKAGE_ARCHS",
170 "MULTILIB_ARCHS"]
171
172 opkg_index_cmd = bb.utils.which(os.getenv('PATH'), "opkg-make-index")
173
174 if not os.path.exists(os.path.join(self.deploy_dir, "Packages")):
175 open(os.path.join(self.deploy_dir, "Packages"), "w").close()
176
177 index_cmds = []
178 for arch_var in arch_vars:
179 archs = self.d.getVar(arch_var, True)
180 if archs is None:
181 continue
182
183 for arch in archs.split():
184 pkgs_dir = os.path.join(self.deploy_dir, arch)
185 pkgs_file = os.path.join(pkgs_dir, "Packages")
186
187 if not os.path.isdir(pkgs_dir):
188 continue
189
190 if not os.path.exists(pkgs_file):
191 open(pkgs_file, "w").close()
192
193 index_cmds.append('%s -r %s -p %s -m %s' %
194 (opkg_index_cmd, pkgs_file, pkgs_file, pkgs_dir))
195
196 if len(index_cmds) == 0:
197 bb.note("There are no packages in %s!" % self.deploy_dir)
198 return
199
200 result = oe.utils.multiprocess_exec(index_cmds, create_index)
201 if result:
202 bb.fatal('%s' % ('\n'.join(result)))
203
204
205
206class DpkgIndexer(Indexer):
207 def _create_configs(self):
208 bb.utils.mkdirhier(self.apt_conf_dir)
209 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "lists", "partial"))
210 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "apt.conf.d"))
211 bb.utils.mkdirhier(os.path.join(self.apt_conf_dir, "preferences.d"))
212
213 with open(os.path.join(self.apt_conf_dir, "preferences"),
214 "w") as prefs_file:
215 pass
216 with open(os.path.join(self.apt_conf_dir, "sources.list"),
217 "w+") as sources_file:
218 pass
219
220 with open(self.apt_conf_file, "w") as apt_conf:
221 with open(os.path.join(self.d.expand("${STAGING_ETCDIR_NATIVE}"),
222 "apt", "apt.conf.sample")) as apt_conf_sample:
223 for line in apt_conf_sample.read().split("\n"):
224 line = re.sub("#ROOTFS#", "/dev/null", line)
225 line = re.sub("#APTCONF#", self.apt_conf_dir, line)
226 apt_conf.write(line + "\n")
227
228 def write_index(self):
229 self.apt_conf_dir = os.path.join(self.d.expand("${APTCONF_TARGET}"),
230 "apt-ftparchive")
231 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
232 self._create_configs()
233
234 os.environ['APT_CONFIG'] = self.apt_conf_file
235
236 pkg_archs = self.d.getVar('PACKAGE_ARCHS', True)
237 if pkg_archs is not None:
238 arch_list = pkg_archs.split()
239 sdk_pkg_archs = self.d.getVar('SDK_PACKAGE_ARCHS', True)
240 if sdk_pkg_archs is not None:
241 for a in sdk_pkg_archs.split():
242 if a not in pkg_archs:
243 arch_list.append(a)
244
245 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS', True) or "").replace('-', '_').split()
246 arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in arch_list)
247
248 apt_ftparchive = bb.utils.which(os.getenv('PATH'), "apt-ftparchive")
249 gzip = bb.utils.which(os.getenv('PATH'), "gzip")
250
251 index_cmds = []
252 deb_dirs_found = False
253 for arch in arch_list:
254 arch_dir = os.path.join(self.deploy_dir, arch)
255 if not os.path.isdir(arch_dir):
256 continue
257
258 cmd = "cd %s; PSEUDO_UNLOAD=1 %s packages . > Packages;" % (arch_dir, apt_ftparchive)
259
260 cmd += "%s -fc Packages > Packages.gz;" % gzip
261
262 with open(os.path.join(arch_dir, "Release"), "w+") as release:
263 release.write("Label: %s\n" % arch)
264
265 cmd += "PSEUDO_UNLOAD=1 %s release . >> Release" % apt_ftparchive
266
267 index_cmds.append(cmd)
268
269 deb_dirs_found = True
270
271 if not deb_dirs_found:
272 bb.note("There are no packages in %s" % self.deploy_dir)
273 return
274
275 result = oe.utils.multiprocess_exec(index_cmds, create_index)
276 if result:
277 bb.fatal('%s' % ('\n'.join(result)))
278
279
280
281class PkgsList(object):
282 __metaclass__ = ABCMeta
283
284 def __init__(self, d, rootfs_dir):
285 self.d = d
286 self.rootfs_dir = rootfs_dir
287
288 @abstractmethod
289 def list(self, format=None):
290 pass
291
292
293class RpmPkgsList(PkgsList):
294 def __init__(self, d, rootfs_dir, arch_var=None, os_var=None):
295 super(RpmPkgsList, self).__init__(d, rootfs_dir)
296
297 self.rpm_cmd = bb.utils.which(os.getenv('PATH'), "rpm")
298 self.image_rpmlib = os.path.join(self.rootfs_dir, 'var/lib/rpm')
299
300 self.ml_prefix_list, self.ml_os_list = \
301 RpmIndexer(d, rootfs_dir).get_ml_prefix_and_os_list(arch_var, os_var)
302
303 # Determine rpm version
304 cmd = "%s --version" % self.rpm_cmd
305 try:
306 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
307 except subprocess.CalledProcessError as e:
308 bb.fatal("Getting rpm version failed. Command '%s' "
309 "returned %d:\n%s" % (cmd, e.returncode, e.output))
310 self.rpm_version = int(output.split()[-1].split('.')[0])
311
312 '''
313 Translate the RPM/Smart format names to the OE multilib format names
314 '''
315 def _pkg_translate_smart_to_oe(self, pkg, arch):
316 new_pkg = pkg
317 new_arch = arch
318 fixed_arch = arch.replace('_', '-')
319 found = 0
320 for mlib in self.ml_prefix_list:
321 for cmp_arch in self.ml_prefix_list[mlib]:
322 fixed_cmp_arch = cmp_arch.replace('_', '-')
323 if fixed_arch == fixed_cmp_arch:
324 if mlib == 'default':
325 new_pkg = pkg
326 new_arch = cmp_arch
327 else:
328 new_pkg = mlib + '-' + pkg
329 # We need to strip off the ${mlib}_ prefix on the arch
330 new_arch = cmp_arch.replace(mlib + '_', '')
331
332 # Workaround for bug 3565. Simply look to see if we
333 # know of a package with that name, if not try again!
334 filename = os.path.join(self.d.getVar('PKGDATA_DIR', True),
335 'runtime-reverse',
336 new_pkg)
337 if os.path.exists(filename):
338 found = 1
339 break
340
341 if found == 1 and fixed_arch == fixed_cmp_arch:
342 break
343 #bb.note('%s, %s -> %s, %s' % (pkg, arch, new_pkg, new_arch))
344 return new_pkg, new_arch
345
346 def _list_pkg_deps(self):
347 cmd = [bb.utils.which(os.getenv('PATH'), "rpmresolve"),
348 "-t", self.image_rpmlib]
349
350 try:
351 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip()
352 except subprocess.CalledProcessError as e:
353 bb.fatal("Cannot get the package dependencies. Command '%s' "
354 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output))
355
356 return output
357
358 def list(self, format=None):
359 if format == "deps":
360 if self.rpm_version == 4:
361 bb.fatal("'deps' format dependency listings are not supported with rpm 4 since rpmresolve does not work")
362 return self._list_pkg_deps()
363
364 cmd = self.rpm_cmd + ' --root ' + self.rootfs_dir
365 cmd += ' -D "_dbpath /var/lib/rpm" -qa'
366 if self.rpm_version == 4:
367 cmd += " --qf '[%{NAME} %{ARCH} %{VERSION}\n]'"
368 else:
369 cmd += " --qf '[%{NAME} %{ARCH} %{VERSION} %{PACKAGEORIGIN}\n]'"
370
371 try:
372 # bb.note(cmd)
373 tmp_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).strip()
374
375 except subprocess.CalledProcessError as e:
376 bb.fatal("Cannot get the installed packages list. Command '%s' "
377 "returned %d:\n%s" % (cmd, e.returncode, e.output))
378
379 output = list()
380 for line in tmp_output.split('\n'):
381 if len(line.strip()) == 0:
382 continue
383 pkg = line.split()[0]
384 arch = line.split()[1]
385 ver = line.split()[2]
386 # Skip GPG keys
387 if pkg == 'gpg-pubkey':
388 continue
389 if self.rpm_version == 4:
390 pkgorigin = "unknown"
391 else:
392 pkgorigin = line.split()[3]
393 new_pkg, new_arch = self._pkg_translate_smart_to_oe(pkg, arch)
394
395 if format == "arch":
396 output.append('%s %s' % (new_pkg, new_arch))
397 elif format == "file":
398 output.append('%s %s %s' % (new_pkg, pkgorigin, new_arch))
399 elif format == "ver":
400 output.append('%s %s %s' % (new_pkg, new_arch, ver))
401 else:
402 output.append('%s' % (new_pkg))
403
404 output.sort()
405
406 return '\n'.join(output)
407
408
409class OpkgPkgsList(PkgsList):
410 def __init__(self, d, rootfs_dir, config_file):
411 super(OpkgPkgsList, self).__init__(d, rootfs_dir)
412
413 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
414 self.opkg_args = "-f %s -o %s " % (config_file, rootfs_dir)
415 self.opkg_args += self.d.getVar("OPKG_ARGS", True)
416
417 def list(self, format=None):
418 opkg_query_cmd = bb.utils.which(os.getenv('PATH'), "opkg-query-helper.py")
419
420 if format == "arch":
421 cmd = "%s %s status | %s -a" % \
422 (self.opkg_cmd, self.opkg_args, opkg_query_cmd)
423 elif format == "file":
424 cmd = "%s %s status | %s -f" % \
425 (self.opkg_cmd, self.opkg_args, opkg_query_cmd)
426 elif format == "ver":
427 cmd = "%s %s status | %s -v" % \
428 (self.opkg_cmd, self.opkg_args, opkg_query_cmd)
429 elif format == "deps":
430 cmd = "%s %s status | %s" % \
431 (self.opkg_cmd, self.opkg_args, opkg_query_cmd)
432 else:
433 cmd = "%s %s list_installed | cut -d' ' -f1" % \
434 (self.opkg_cmd, self.opkg_args)
435
436 try:
437 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).strip()
438 except subprocess.CalledProcessError as e:
439 bb.fatal("Cannot get the installed packages list. Command '%s' "
440 "returned %d:\n%s" % (cmd, e.returncode, e.output))
441
442 if output and format == "file":
443 tmp_output = ""
444 for line in output.split('\n'):
445 pkg, pkg_file, pkg_arch = line.split()
446 full_path = os.path.join(self.rootfs_dir, pkg_arch, pkg_file)
447 if os.path.exists(full_path):
448 tmp_output += "%s %s %s\n" % (pkg, full_path, pkg_arch)
449 else:
450 tmp_output += "%s %s %s\n" % (pkg, pkg_file, pkg_arch)
451
452 output = tmp_output
453
454 return output
455
456
457class DpkgPkgsList(PkgsList):
458 def list(self, format=None):
459 cmd = [bb.utils.which(os.getenv('PATH'), "dpkg-query"),
460 "--admindir=%s/var/lib/dpkg" % self.rootfs_dir,
461 "-W"]
462
463 if format == "arch":
464 cmd.append("-f=${Package} ${PackageArch}\n")
465 elif format == "file":
466 cmd.append("-f=${Package} ${Package}_${Version}_${Architecture}.deb ${PackageArch}\n")
467 elif format == "ver":
468 cmd.append("-f=${Package} ${PackageArch} ${Version}\n")
469 elif format == "deps":
470 cmd.append("-f=Package: ${Package}\nDepends: ${Depends}\nRecommends: ${Recommends}\n\n")
471 else:
472 cmd.append("-f=${Package}\n")
473
474 try:
475 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip()
476 except subprocess.CalledProcessError as e:
477 bb.fatal("Cannot get the installed packages list. Command '%s' "
478 "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output))
479
480 if format == "file":
481 tmp_output = ""
482 for line in tuple(output.split('\n')):
483 if not line.strip():
484 continue
485 pkg, pkg_file, pkg_arch = line.split()
486 full_path = os.path.join(self.rootfs_dir, pkg_arch, pkg_file)
487 if os.path.exists(full_path):
488 tmp_output += "%s %s %s\n" % (pkg, full_path, pkg_arch)
489 else:
490 tmp_output += "%s %s %s\n" % (pkg, pkg_file, pkg_arch)
491
492 output = tmp_output
493 elif format == "deps":
494 opkg_query_cmd = bb.utils.which(os.getenv('PATH'), "opkg-query-helper.py")
495 file_out = tempfile.NamedTemporaryFile()
496 file_out.write(output)
497 file_out.flush()
498
499 try:
500 output = subprocess.check_output("cat %s | %s" %
501 (file_out.name, opkg_query_cmd),
502 stderr=subprocess.STDOUT,
503 shell=True)
504 except subprocess.CalledProcessError as e:
505 file_out.close()
506 bb.fatal("Cannot compute packages dependencies. Command '%s' "
507 "returned %d:\n%s" % (e.cmd, e.returncode, e.output))
508
509 file_out.close()
510
511 return output
512
513
514class PackageManager(object):
515 """
516 This is an abstract class. Do not instantiate this directly.
517 """
518 __metaclass__ = ABCMeta
519
520 def __init__(self, d):
521 self.d = d
522 self.deploy_dir = None
523 self.deploy_lock = None
524 self.feed_uris = self.d.getVar('PACKAGE_FEED_URIS', True) or ""
525 self.feed_prefix = self.d.getVar('PACKAGE_FEED_PREFIX', True) or ""
526
527 """
528 Update the package manager package database.
529 """
530 @abstractmethod
531 def update(self):
532 pass
533
534 """
535 Install a list of packages. 'pkgs' is a list object. If 'attempt_only' is
536 True, installation failures are ignored.
537 """
538 @abstractmethod
539 def install(self, pkgs, attempt_only=False):
540 pass
541
542 """
543 Remove a list of packages. 'pkgs' is a list object. If 'with_dependencies'
544 is False, the any dependencies are left in place.
545 """
546 @abstractmethod
547 def remove(self, pkgs, with_dependencies=True):
548 pass
549
550 """
551 This function creates the index files
552 """
553 @abstractmethod
554 def write_index(self):
555 pass
556
557 @abstractmethod
558 def remove_packaging_data(self):
559 pass
560
561 @abstractmethod
562 def list_installed(self, format=None):
563 pass
564
565 @abstractmethod
566 def insert_feeds_uris(self):
567 pass
568
569 """
570 Install complementary packages based upon the list of currently installed
571 packages e.g. locales, *-dev, *-dbg, etc. This will only attempt to install
572 these packages, if they don't exist then no error will occur. Note: every
573 backend needs to call this function explicitly after the normal package
574 installation
575 """
576 def install_complementary(self, globs=None):
577 # we need to write the list of installed packages to a file because the
578 # oe-pkgdata-util reads it from a file
579 installed_pkgs_file = os.path.join(self.d.getVar('WORKDIR', True),
580 "installed_pkgs.txt")
581 with open(installed_pkgs_file, "w+") as installed_pkgs:
582 installed_pkgs.write(self.list_installed("arch"))
583
584 if globs is None:
585 globs = self.d.getVar('IMAGE_INSTALL_COMPLEMENTARY', True)
586 split_linguas = set()
587
588 for translation in self.d.getVar('IMAGE_LINGUAS', True).split():
589 split_linguas.add(translation)
590 split_linguas.add(translation.split('-')[0])
591
592 split_linguas = sorted(split_linguas)
593
594 for lang in split_linguas:
595 globs += " *-locale-%s" % lang
596
597 if globs is None:
598 return
599
600 cmd = [bb.utils.which(os.getenv('PATH'), "oe-pkgdata-util"),
601 "-p", self.d.getVar('PKGDATA_DIR', True), "glob", installed_pkgs_file,
602 globs]
603 exclude = self.d.getVar('PACKAGE_EXCLUDE_COMPLEMENTARY', True)
604 if exclude:
605 cmd.extend(['-x', exclude])
606 try:
607 bb.note("Installing complementary packages ...")
608 complementary_pkgs = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
609 except subprocess.CalledProcessError as e:
610 bb.fatal("Could not compute complementary packages list. Command "
611 "'%s' returned %d:\n%s" %
612 (' '.join(cmd), e.returncode, e.output))
613
614 self.install(complementary_pkgs.split(), attempt_only=True)
615
616 def deploy_dir_lock(self):
617 if self.deploy_dir is None:
618 raise RuntimeError("deploy_dir is not set!")
619
620 lock_file_name = os.path.join(self.deploy_dir, "deploy.lock")
621
622 self.deploy_lock = bb.utils.lockfile(lock_file_name)
623
624 def deploy_dir_unlock(self):
625 if self.deploy_lock is None:
626 return
627
628 bb.utils.unlockfile(self.deploy_lock)
629
630 self.deploy_lock = None
631
632
633class RpmPM(PackageManager):
634 def __init__(self,
635 d,
636 target_rootfs,
637 target_vendor,
638 task_name='target',
639 providename=None,
640 arch_var=None,
641 os_var=None):
642 super(RpmPM, self).__init__(d)
643 self.target_rootfs = target_rootfs
644 self.target_vendor = target_vendor
645 self.task_name = task_name
646 self.providename = providename
647 self.fullpkglist = list()
648 self.deploy_dir = self.d.getVar('DEPLOY_DIR_RPM', True)
649 self.etcrpm_dir = os.path.join(self.target_rootfs, "etc/rpm")
650 self.install_dir_name = "oe_install"
651 self.install_dir_path = os.path.join(self.target_rootfs, self.install_dir_name)
652 self.rpm_cmd = bb.utils.which(os.getenv('PATH'), "rpm")
653 self.smart_cmd = bb.utils.which(os.getenv('PATH'), "smart")
654 self.smart_opt = "--log-level=warning --data-dir=" + os.path.join(target_rootfs,
655 'var/lib/smart')
656 self.scriptlet_wrapper = self.d.expand('${WORKDIR}/scriptlet_wrapper')
657 self.solution_manifest = self.d.expand('${T}/saved/%s_solution' %
658 self.task_name)
659 self.saved_rpmlib = self.d.expand('${T}/saved/%s' % self.task_name)
660 self.image_rpmlib = os.path.join(self.target_rootfs, 'var/lib/rpm')
661
662 if not os.path.exists(self.d.expand('${T}/saved')):
663 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
664
665 self.indexer = RpmIndexer(self.d, self.deploy_dir)
666 self.pkgs_list = RpmPkgsList(self.d, self.target_rootfs, arch_var, os_var)
667 self.rpm_version = self.pkgs_list.rpm_version
668
669 self.ml_prefix_list, self.ml_os_list = self.indexer.get_ml_prefix_and_os_list(arch_var, os_var)
670
671 def insert_feeds_uris(self):
672 if self.feed_uris == "":
673 return
674
675 # List must be prefered to least preferred order
676 default_platform_extra = set()
677 platform_extra = set()
678 bbextendvariant = self.d.getVar('BBEXTENDVARIANT', True) or ""
679 for mlib in self.ml_os_list:
680 for arch in self.ml_prefix_list[mlib]:
681 plt = arch.replace('-', '_') + '-.*-' + self.ml_os_list[mlib]
682 if mlib == bbextendvariant:
683 default_platform_extra.add(plt)
684 else:
685 platform_extra.add(plt)
686
687 platform_extra = platform_extra.union(default_platform_extra)
688
689 arch_list = []
690 for canonical_arch in platform_extra:
691 arch = canonical_arch.split('-')[0]
692 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
693 continue
694 arch_list.append(arch)
695
696 uri_iterator = 0
697 channel_priority = 10 + 5 * len(self.feed_uris.split()) * len(arch_list)
698
699 for uri in self.feed_uris.split():
700 full_uri = uri
701 if self.feed_prefix:
702 full_uri = os.path.join(uri, self.feed_prefix)
703 for arch in arch_list:
704 bb.note('Note: adding Smart channel url%d%s (%s)' %
705 (uri_iterator, arch, channel_priority))
706 self._invoke_smart('channel --add url%d-%s type=rpm-md baseurl=%s/%s -y'
707 % (uri_iterator, arch, full_uri, arch))
708 self._invoke_smart('channel --set url%d-%s priority=%d' %
709 (uri_iterator, arch, channel_priority))
710 channel_priority -= 5
711 uri_iterator += 1
712
713 '''
714 Create configs for rpm and smart, and multilib is supported
715 '''
716 def create_configs(self):
717 target_arch = self.d.getVar('TARGET_ARCH', True)
718 platform = '%s%s-%s' % (target_arch.replace('-', '_'),
719 self.target_vendor,
720 self.ml_os_list['default'])
721
722 # List must be prefered to least preferred order
723 default_platform_extra = list()
724 platform_extra = list()
725 bbextendvariant = self.d.getVar('BBEXTENDVARIANT', True) or ""
726 for mlib in self.ml_os_list:
727 for arch in self.ml_prefix_list[mlib]:
728 plt = arch.replace('-', '_') + '-.*-' + self.ml_os_list[mlib]
729 if mlib == bbextendvariant:
730 if plt not in default_platform_extra:
731 default_platform_extra.append(plt)
732 else:
733 if plt not in platform_extra:
734 platform_extra.append(plt)
735 platform_extra = default_platform_extra + platform_extra
736
737 self._create_configs(platform, platform_extra)
738
739 def _invoke_smart(self, args):
740 cmd = "%s %s %s" % (self.smart_cmd, self.smart_opt, args)
741 # bb.note(cmd)
742 try:
743 complementary_pkgs = subprocess.check_output(cmd,
744 stderr=subprocess.STDOUT,
745 shell=True)
746 # bb.note(complementary_pkgs)
747 return complementary_pkgs
748 except subprocess.CalledProcessError as e:
749 bb.fatal("Could not invoke smart. Command "
750 "'%s' returned %d:\n%s" % (cmd, e.returncode, e.output))
751
752 def _search_pkg_name_in_feeds(self, pkg, feed_archs):
753 for arch in feed_archs:
754 arch = arch.replace('-', '_')
755 regex_match = re.compile(r"^%s-[^-]*-[^-]*@%s$" % \
756 (re.escape(pkg), re.escape(arch)))
757 for p in self.fullpkglist:
758 if regex_match.match(p) is not None:
759 # First found is best match
760 # bb.note('%s -> %s' % (pkg, pkg + '@' + arch))
761 return pkg + '@' + arch
762
763 # Search provides if not found by pkgname.
764 bb.note('Not found %s by name, searching provides ...' % pkg)
765 cmd = "%s %s query --provides %s --show-format='$name-$version'" % \
766 (self.smart_cmd, self.smart_opt, pkg)
767 cmd += " | sed -ne 's/ *Provides://p'"
768 bb.note('cmd: %s' % cmd)
769 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
770 # Found a provider
771 if output:
772 bb.note('Found providers for %s: %s' % (pkg, output))
773 for p in output.split():
774 for arch in feed_archs:
775 arch = arch.replace('-', '_')
776 if p.rstrip().endswith('@' + arch):
777 return p
778
779 return ""
780
781 '''
782 Translate the OE multilib format names to the RPM/Smart format names
783 It searched the RPM/Smart format names in probable multilib feeds first,
784 and then searched the default base feed.
785 '''
786 def _pkg_translate_oe_to_smart(self, pkgs, attempt_only=False):
787 new_pkgs = list()
788
789 for pkg in pkgs:
790 new_pkg = pkg
791 # Search new_pkg in probable multilibs first
792 for mlib in self.ml_prefix_list:
793 # Jump the default archs
794 if mlib == 'default':
795 continue
796
797 subst = pkg.replace(mlib + '-', '')
798 # if the pkg in this multilib feed
799 if subst != pkg:
800 feed_archs = self.ml_prefix_list[mlib]
801 new_pkg = self._search_pkg_name_in_feeds(subst, feed_archs)
802 if not new_pkg:
803 # Failed to translate, package not found!
804 err_msg = '%s not found in the %s feeds (%s).\n' % \
805 (pkg, mlib, " ".join(feed_archs))
806 if not attempt_only:
807 err_msg += " ".join(self.fullpkglist)
808 bb.fatal(err_msg)
809 bb.warn(err_msg)
810 else:
811 new_pkgs.append(new_pkg)
812
813 break
814
815 # Apparently not a multilib package...
816 if pkg == new_pkg:
817 # Search new_pkg in default archs
818 default_archs = self.ml_prefix_list['default']
819 new_pkg = self._search_pkg_name_in_feeds(pkg, default_archs)
820 if not new_pkg:
821 err_msg = '%s not found in the base feeds (%s).\n' % \
822 (pkg, ' '.join(default_archs))
823 if not attempt_only:
824 err_msg += " ".join(self.fullpkglist)
825 bb.fatal(err_msg)
826 bb.warn(err_msg)
827 else:
828 new_pkgs.append(new_pkg)
829
830 return new_pkgs
831
832 def _create_configs(self, platform, platform_extra):
833 # Setup base system configuration
834 bb.note("configuring RPM platform settings")
835
836 # Configure internal RPM environment when using Smart
837 os.environ['RPM_ETCRPM'] = self.etcrpm_dir
838 bb.utils.mkdirhier(self.etcrpm_dir)
839
840 # Setup temporary directory -- install...
841 if os.path.exists(self.install_dir_path):
842 bb.utils.remove(self.install_dir_path, True)
843 bb.utils.mkdirhier(os.path.join(self.install_dir_path, 'tmp'))
844
845 channel_priority = 5
846 platform_dir = os.path.join(self.etcrpm_dir, "platform")
847 sdkos = self.d.getVar("SDK_OS", True)
848 with open(platform_dir, "w+") as platform_fd:
849 platform_fd.write(platform + '\n')
850 for pt in platform_extra:
851 channel_priority += 5
852 if sdkos:
853 tmp = re.sub("-%s$" % sdkos, "-%s\n" % sdkos, pt)
854 tmp = re.sub("-linux.*$", "-linux.*\n", tmp)
855 platform_fd.write(tmp)
856
857 # Tell RPM that the "/" directory exist and is available
858 bb.note("configuring RPM system provides")
859 sysinfo_dir = os.path.join(self.etcrpm_dir, "sysinfo")
860 bb.utils.mkdirhier(sysinfo_dir)
861 with open(os.path.join(sysinfo_dir, "Dirnames"), "w+") as dirnames:
862 dirnames.write("/\n")
863
864 if self.providename:
865 providename_dir = os.path.join(sysinfo_dir, "Providename")
866 if not os.path.exists(providename_dir):
867 providename_content = '\n'.join(self.providename)
868 providename_content += '\n'
869 open(providename_dir, "w+").write(providename_content)
870
871 # Configure RPM... we enforce these settings!
872 bb.note("configuring RPM DB settings")
873 # After change the __db.* cache size, log file will not be
874 # generated automatically, that will raise some warnings,
875 # so touch a bare log for rpm write into it.
876 if self.rpm_version == 5:
877 rpmlib_log = os.path.join(self.image_rpmlib, 'log', 'log.0000000001')
878 if not os.path.exists(rpmlib_log):
879 bb.utils.mkdirhier(os.path.join(self.image_rpmlib, 'log'))
880 open(rpmlib_log, 'w+').close()
881
882 DB_CONFIG_CONTENT = "# ================ Environment\n" \
883 "set_data_dir .\n" \
884 "set_create_dir .\n" \
885 "set_lg_dir ./log\n" \
886 "set_tmp_dir ./tmp\n" \
887 "set_flags db_log_autoremove on\n" \
888 "\n" \
889 "# -- thread_count must be >= 8\n" \
890 "set_thread_count 64\n" \
891 "\n" \
892 "# ================ Logging\n" \
893 "\n" \
894 "# ================ Memory Pool\n" \
895 "set_cachesize 0 1048576 0\n" \
896 "set_mp_mmapsize 268435456\n" \
897 "\n" \
898 "# ================ Locking\n" \
899 "set_lk_max_locks 16384\n" \
900 "set_lk_max_lockers 16384\n" \
901 "set_lk_max_objects 16384\n" \
902 "mutex_set_max 163840\n" \
903 "\n" \
904 "# ================ Replication\n"
905
906 db_config_dir = os.path.join(self.image_rpmlib, 'DB_CONFIG')
907 if not os.path.exists(db_config_dir):
908 open(db_config_dir, 'w+').write(DB_CONFIG_CONTENT)
909
910 # Create database so that smart doesn't complain (lazy init)
911 opt = "-qa"
912 if self.rpm_version == 4:
913 opt = "--initdb"
914 cmd = "%s --root %s --dbpath /var/lib/rpm %s > /dev/null" % (
915 self.rpm_cmd, self.target_rootfs, opt)
916 try:
917 subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
918 except subprocess.CalledProcessError as e:
919 bb.fatal("Create rpm database failed. Command '%s' "
920 "returned %d:\n%s" % (cmd, e.returncode, e.output))
921 # Import GPG key to RPM database of the target system
922 if self.d.getVar('RPM_SIGN_PACKAGES', True) == '1':
923 pubkey_path = self.d.getVar('RPM_GPG_PUBKEY', True)
924 cmd = "%s --root %s --dbpath /var/lib/rpm --import %s > /dev/null" % (
925 self.rpm_cmd, self.target_rootfs, pubkey_path)
926 subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
927
928 # Configure smart
929 bb.note("configuring Smart settings")
930 bb.utils.remove(os.path.join(self.target_rootfs, 'var/lib/smart'),
931 True)
932 self._invoke_smart('config --set rpm-root=%s' % self.target_rootfs)
933 self._invoke_smart('config --set rpm-dbpath=/var/lib/rpm')
934 self._invoke_smart('config --set rpm-extra-macros._var=%s' %
935 self.d.getVar('localstatedir', True))
936 cmd = "config --set rpm-extra-macros._tmppath=/%s/tmp" % (self.install_dir_name)
937
938 prefer_color = self.d.getVar('RPM_PREFER_ELF_ARCH', True)
939 if prefer_color:
940 if prefer_color not in ['0', '1', '2', '4']:
941 bb.fatal("Invalid RPM_PREFER_ELF_ARCH: %s, it should be one of:\n"
942 "\t1: ELF32 wins\n"
943 "\t2: ELF64 wins\n"
944 "\t4: ELF64 N32 wins (mips64 or mips64el only)" %
945 prefer_color)
946 if prefer_color == "4" and self.d.getVar("TUNE_ARCH", True) not in \
947 ['mips64', 'mips64el']:
948 bb.fatal("RPM_PREFER_ELF_ARCH = \"4\" is for mips64 or mips64el "
949 "only.")
950 self._invoke_smart('config --set rpm-extra-macros._prefer_color=%s'
951 % prefer_color)
952
953 self._invoke_smart(cmd)
954 self._invoke_smart('config --set rpm-ignoresize=1')
955
956 # Write common configuration for host and target usage
957 self._invoke_smart('config --set rpm-nolinktos=1')
958 self._invoke_smart('config --set rpm-noparentdirs=1')
959 check_signature = self.d.getVar('RPM_CHECK_SIGNATURES', True)
960 if check_signature and check_signature.strip() == "0":
961 self._invoke_smart('config --set rpm-check-signatures=false')
962 for i in self.d.getVar('BAD_RECOMMENDATIONS', True).split():
963 self._invoke_smart('flag --set ignore-recommends %s' % i)
964
965 # Do the following configurations here, to avoid them being
966 # saved for field upgrade
967 if self.d.getVar('NO_RECOMMENDATIONS', True).strip() == "1":
968 self._invoke_smart('config --set ignore-all-recommends=1')
969 pkg_exclude = self.d.getVar('PACKAGE_EXCLUDE', True) or ""
970 for i in pkg_exclude.split():
971 self._invoke_smart('flag --set exclude-packages %s' % i)
972
973 # Optional debugging
974 # self._invoke_smart('config --set rpm-log-level=debug')
975 # cmd = 'config --set rpm-log-file=/tmp/smart-debug-logfile'
976 # self._invoke_smart(cmd)
977 ch_already_added = []
978 for canonical_arch in platform_extra:
979 arch = canonical_arch.split('-')[0]
980 arch_channel = os.path.join(self.deploy_dir, arch)
981 if os.path.exists(arch_channel) and not arch in ch_already_added:
982 bb.note('Note: adding Smart channel %s (%s)' %
983 (arch, channel_priority))
984 self._invoke_smart('channel --add %s type=rpm-md baseurl=%s -y'
985 % (arch, arch_channel))
986 self._invoke_smart('channel --set %s priority=%d' %
987 (arch, channel_priority))
988 channel_priority -= 5
989
990 ch_already_added.append(arch)
991
992 bb.note('adding Smart RPM DB channel')
993 self._invoke_smart('channel --add rpmsys type=rpm-sys -y')
994
995 # Construct install scriptlet wrapper.
996 # Scripts need to be ordered when executed, this ensures numeric order.
997 # If we ever run into needing more the 899 scripts, we'll have to.
998 # change num to start with 1000.
999 #
1000 if self.rpm_version == 4:
1001 scriptletcmd = "$2 $3 $4\n"
1002 scriptpath = "$3"
1003 else:
1004 scriptletcmd = "$2 $1/$3 $4\n"
1005 scriptpath = "$1/$3"
1006
1007 SCRIPTLET_FORMAT = "#!/bin/bash\n" \
1008 "\n" \
1009 "export PATH=%s\n" \
1010 "export D=%s\n" \
1011 'export OFFLINE_ROOT="$D"\n' \
1012 'export IPKG_OFFLINE_ROOT="$D"\n' \
1013 'export OPKG_OFFLINE_ROOT="$D"\n' \
1014 "export INTERCEPT_DIR=%s\n" \
1015 "export NATIVE_ROOT=%s\n" \
1016 "\n" \
1017 + scriptletcmd + \
1018 "if [ $? -ne 0 ]; then\n" \
1019 " if [ $4 -eq 1 ]; then\n" \
1020 " mkdir -p $1/etc/rpm-postinsts\n" \
1021 " num=100\n" \
1022 " while [ -e $1/etc/rpm-postinsts/${num}-* ]; do num=$((num + 1)); done\n" \
1023 " name=`head -1 " + scriptpath + " | cut -d\' \' -f 2`\n" \
1024 ' echo "#!$2" > $1/etc/rpm-postinsts/${num}-${name}\n' \
1025 ' echo "# Arg: $4" >> $1/etc/rpm-postinsts/${num}-${name}\n' \
1026 " cat " + scriptpath + " >> $1/etc/rpm-postinsts/${num}-${name}\n" \
1027 " chmod +x $1/etc/rpm-postinsts/${num}-${name}\n" \
1028 " else\n" \
1029 ' echo "Error: pre/post remove scriptlet failed"\n' \
1030 " fi\n" \
1031 "fi\n"
1032
1033 intercept_dir = self.d.expand('${WORKDIR}/intercept_scripts')
1034 native_root = self.d.getVar('STAGING_DIR_NATIVE', True)
1035 scriptlet_content = SCRIPTLET_FORMAT % (os.environ['PATH'],
1036 self.target_rootfs,
1037 intercept_dir,
1038 native_root)
1039 open(self.scriptlet_wrapper, 'w+').write(scriptlet_content)
1040
1041 bb.note("Note: configuring RPM cross-install scriptlet_wrapper")
1042 os.chmod(self.scriptlet_wrapper, 0755)
1043 cmd = 'config --set rpm-extra-macros._cross_scriptlet_wrapper=%s' % \
1044 self.scriptlet_wrapper
1045 self._invoke_smart(cmd)
1046
1047 # Debug to show smart config info
1048 # bb.note(self._invoke_smart('config --show'))
1049
1050 def update(self):
1051 self._invoke_smart('update rpmsys')
1052
1053 '''
1054 Install pkgs with smart, the pkg name is oe format
1055 '''
1056 def install(self, pkgs, attempt_only=False):
1057
1058 if not pkgs:
1059 bb.note("There are no packages to install")
1060 return
1061 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1062 pkgs = self._pkg_translate_oe_to_smart(pkgs, attempt_only)
1063
1064 if not attempt_only:
1065 bb.note('to be installed: %s' % ' '.join(pkgs))
1066 cmd = "%s %s install -y %s" % \
1067 (self.smart_cmd, self.smart_opt, ' '.join(pkgs))
1068 bb.note(cmd)
1069 else:
1070 bb.note('installing attempt only packages...')
1071 bb.note('Attempting %s' % ' '.join(pkgs))
1072 cmd = "%s %s install --attempt -y %s" % \
1073 (self.smart_cmd, self.smart_opt, ' '.join(pkgs))
1074 try:
1075 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1076 bb.note(output)
1077 except subprocess.CalledProcessError as e:
1078 bb.fatal("Unable to install packages. Command '%s' "
1079 "returned %d:\n%s" % (cmd, e.returncode, e.output))
1080
1081 '''
1082 Remove pkgs with smart, the pkg name is smart/rpm format
1083 '''
1084 def remove(self, pkgs, with_dependencies=True):
1085 bb.note('to be removed: ' + ' '.join(pkgs))
1086
1087 if not with_dependencies:
1088 cmd = "%s -e --nodeps " % self.rpm_cmd
1089 cmd += "--root=%s " % self.target_rootfs
1090 cmd += "--dbpath=/var/lib/rpm "
1091 cmd += "--define='_cross_scriptlet_wrapper %s' " % \
1092 self.scriptlet_wrapper
1093 cmd += "--define='_tmppath /%s/tmp' %s" % (self.install_dir_name, ' '.join(pkgs))
1094 else:
1095 # for pkg in pkgs:
1096 # bb.note('Debug: What required: %s' % pkg)
1097 # bb.note(self._invoke_smart('query %s --show-requiredby' % pkg))
1098
1099 cmd = "%s %s remove -y %s" % (self.smart_cmd,
1100 self.smart_opt,
1101 ' '.join(pkgs))
1102
1103 try:
1104 bb.note(cmd)
1105 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1106 bb.note(output)
1107 except subprocess.CalledProcessError as e:
1108 bb.note("Unable to remove packages. Command '%s' "
1109 "returned %d:\n%s" % (cmd, e.returncode, e.output))
1110
1111 def upgrade(self):
1112 bb.note('smart upgrade')
1113 self._invoke_smart('upgrade')
1114
1115 def write_index(self):
1116 result = self.indexer.write_index()
1117
1118 if result is not None:
1119 bb.fatal(result)
1120
1121 def remove_packaging_data(self):
1122 bb.utils.remove(self.image_rpmlib, True)
1123 bb.utils.remove(os.path.join(self.target_rootfs, 'var/lib/smart'),
1124 True)
1125 bb.utils.remove(os.path.join(self.target_rootfs, 'var/lib/opkg'), True)
1126
1127 # remove temp directory
1128 bb.utils.remove(self.install_dir_path, True)
1129
1130 def backup_packaging_data(self):
1131 # Save the rpmlib for increment rpm image generation
1132 if os.path.exists(self.saved_rpmlib):
1133 bb.utils.remove(self.saved_rpmlib, True)
1134 shutil.copytree(self.image_rpmlib,
1135 self.saved_rpmlib,
1136 symlinks=True)
1137
1138 def recovery_packaging_data(self):
1139 # Move the rpmlib back
1140 if os.path.exists(self.saved_rpmlib):
1141 if os.path.exists(self.image_rpmlib):
1142 bb.utils.remove(self.image_rpmlib, True)
1143
1144 bb.note('Recovery packaging data')
1145 shutil.copytree(self.saved_rpmlib,
1146 self.image_rpmlib,
1147 symlinks=True)
1148
1149 def list_installed(self, format=None):
1150 return self.pkgs_list.list(format)
1151
1152 '''
1153 If incremental install, we need to determine what we've got,
1154 what we need to add, and what to remove...
1155 The dump_install_solution will dump and save the new install
1156 solution.
1157 '''
1158 def dump_install_solution(self, pkgs):
1159 bb.note('creating new install solution for incremental install')
1160 if len(pkgs) == 0:
1161 return
1162
1163 pkgs = self._pkg_translate_oe_to_smart(pkgs, False)
1164 install_pkgs = list()
1165
1166 cmd = "%s %s install -y --dump %s 2>%s" % \
1167 (self.smart_cmd,
1168 self.smart_opt,
1169 ' '.join(pkgs),
1170 self.solution_manifest)
1171 try:
1172 # Disable rpmsys channel for the fake install
1173 self._invoke_smart('channel --disable rpmsys')
1174
1175 subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1176 with open(self.solution_manifest, 'r') as manifest:
1177 for pkg in manifest.read().split('\n'):
1178 if '@' in pkg:
1179 install_pkgs.append(pkg)
1180 except subprocess.CalledProcessError as e:
1181 bb.note("Unable to dump install packages. Command '%s' "
1182 "returned %d:\n%s" % (cmd, e.returncode, e.output))
1183 # Recovery rpmsys channel
1184 self._invoke_smart('channel --enable rpmsys')
1185 return install_pkgs
1186
1187 '''
1188 If incremental install, we need to determine what we've got,
1189 what we need to add, and what to remove...
1190 The load_old_install_solution will load the previous install
1191 solution
1192 '''
1193 def load_old_install_solution(self):
1194 bb.note('load old install solution for incremental install')
1195 installed_pkgs = list()
1196 if not os.path.exists(self.solution_manifest):
1197 bb.note('old install solution not exist')
1198 return installed_pkgs
1199
1200 with open(self.solution_manifest, 'r') as manifest:
1201 for pkg in manifest.read().split('\n'):
1202 if '@' in pkg:
1203 installed_pkgs.append(pkg.strip())
1204
1205 return installed_pkgs
1206
1207 '''
1208 Dump all available packages in feeds, it should be invoked after the
1209 newest rpm index was created
1210 '''
1211 def dump_all_available_pkgs(self):
1212 available_manifest = self.d.expand('${T}/saved/available_pkgs.txt')
1213 available_pkgs = list()
1214 cmd = "%s %s query --output %s" % \
1215 (self.smart_cmd, self.smart_opt, available_manifest)
1216 try:
1217 subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1218 with open(available_manifest, 'r') as manifest:
1219 for pkg in manifest.read().split('\n'):
1220 if '@' in pkg:
1221 available_pkgs.append(pkg.strip())
1222 except subprocess.CalledProcessError as e:
1223 bb.note("Unable to list all available packages. Command '%s' "
1224 "returned %d:\n%s" % (cmd, e.returncode, e.output))
1225
1226 self.fullpkglist = available_pkgs
1227
1228 return
1229
1230 def save_rpmpostinst(self, pkg):
1231 mlibs = (self.d.getVar('MULTILIB_GLOBAL_VARIANTS', False) or "").split()
1232
1233 new_pkg = pkg
1234 # Remove any multilib prefix from the package name
1235 for mlib in mlibs:
1236 if mlib in pkg:
1237 new_pkg = pkg.replace(mlib + '-', '')
1238 break
1239
1240 bb.note(' * postponing %s' % new_pkg)
1241 saved_dir = self.target_rootfs + self.d.expand('${sysconfdir}/rpm-postinsts/') + new_pkg
1242
1243 cmd = self.rpm_cmd + ' -q --scripts --root ' + self.target_rootfs
1244 cmd += ' --dbpath=/var/lib/rpm ' + new_pkg
1245 cmd += ' | sed -n -e "/^postinstall scriptlet (using .*):$/,/^.* scriptlet (using .*):$/ {/.*/p}"'
1246 cmd += ' | sed -e "/postinstall scriptlet (using \(.*\)):$/d"'
1247 cmd += ' -e "/^.* scriptlet (using .*):$/d" > %s' % saved_dir
1248
1249 try:
1250 bb.note(cmd)
1251 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).strip()
1252 bb.note(output)
1253 os.chmod(saved_dir, 0755)
1254 except subprocess.CalledProcessError as e:
1255 bb.fatal("Invoke save_rpmpostinst failed. Command '%s' "
1256 "returned %d:\n%s" % (cmd, e.returncode, e.output))
1257
1258 '''Write common configuration for target usage'''
1259 def rpm_setup_smart_target_config(self):
1260 bb.utils.remove(os.path.join(self.target_rootfs, 'var/lib/smart'),
1261 True)
1262
1263 self._invoke_smart('config --set rpm-nolinktos=1')
1264 self._invoke_smart('config --set rpm-noparentdirs=1')
1265 for i in self.d.getVar('BAD_RECOMMENDATIONS', True).split():
1266 self._invoke_smart('flag --set ignore-recommends %s' % i)
1267 self._invoke_smart('channel --add rpmsys type=rpm-sys -y')
1268
1269 '''
1270 The rpm db lock files were produced after invoking rpm to query on
1271 build system, and they caused the rpm on target didn't work, so we
1272 need to unlock the rpm db by removing the lock files.
1273 '''
1274 def unlock_rpm_db(self):
1275 # Remove rpm db lock files
1276 rpm_db_locks = glob.glob('%s/var/lib/rpm/__db.*' % self.target_rootfs)
1277 for f in rpm_db_locks:
1278 bb.utils.remove(f, True)
1279
1280
1281class OpkgPM(PackageManager):
1282 def __init__(self, d, target_rootfs, config_file, archs, task_name='target'):
1283 super(OpkgPM, self).__init__(d)
1284
1285 self.target_rootfs = target_rootfs
1286 self.config_file = config_file
1287 self.pkg_archs = archs
1288 self.task_name = task_name
1289
1290 self.deploy_dir = self.d.getVar("DEPLOY_DIR_IPK", True)
1291 self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock")
1292 self.opkg_cmd = bb.utils.which(os.getenv('PATH'), "opkg")
1293 self.opkg_args = "--volatile-cache -f %s -o %s " % (self.config_file, target_rootfs)
1294 self.opkg_args += self.d.getVar("OPKG_ARGS", True)
1295
1296 opkg_lib_dir = self.d.getVar('OPKGLIBDIR', True)
1297 if opkg_lib_dir[0] == "/":
1298 opkg_lib_dir = opkg_lib_dir[1:]
1299
1300 self.opkg_dir = os.path.join(target_rootfs, opkg_lib_dir, "opkg")
1301
1302 bb.utils.mkdirhier(self.opkg_dir)
1303
1304 self.saved_opkg_dir = self.d.expand('${T}/saved/%s' % self.task_name)
1305 if not os.path.exists(self.d.expand('${T}/saved')):
1306 bb.utils.mkdirhier(self.d.expand('${T}/saved'))
1307
1308 if (self.d.getVar('BUILD_IMAGES_FROM_FEEDS', True) or "") != "1":
1309 self._create_config()
1310 else:
1311 self._create_custom_config()
1312
1313 self.indexer = OpkgIndexer(self.d, self.deploy_dir)
1314
1315 """
1316 This function will change a package's status in /var/lib/opkg/status file.
1317 If 'packages' is None then the new_status will be applied to all
1318 packages
1319 """
1320 def mark_packages(self, status_tag, packages=None):
1321 status_file = os.path.join(self.opkg_dir, "status")
1322
1323 with open(status_file, "r") as sf:
1324 with open(status_file + ".tmp", "w+") as tmp_sf:
1325 if packages is None:
1326 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1327 r"Package: \1\n\2Status: \3%s" % status_tag,
1328 sf.read()))
1329 else:
1330 if type(packages).__name__ != "list":
1331 raise TypeError("'packages' should be a list object")
1332
1333 status = sf.read()
1334 for pkg in packages:
1335 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1336 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1337 status)
1338
1339 tmp_sf.write(status)
1340
1341 os.rename(status_file + ".tmp", status_file)
1342
1343 def _create_custom_config(self):
1344 bb.note("Building from feeds activated!")
1345
1346 with open(self.config_file, "w+") as config_file:
1347 priority = 1
1348 for arch in self.pkg_archs.split():
1349 config_file.write("arch %s %d\n" % (arch, priority))
1350 priority += 5
1351
1352 for line in (self.d.getVar('IPK_FEED_URIS', True) or "").split():
1353 feed_match = re.match("^[ \t]*(.*)##([^ \t]*)[ \t]*$", line)
1354
1355 if feed_match is not None:
1356 feed_name = feed_match.group(1)
1357 feed_uri = feed_match.group(2)
1358
1359 bb.note("Add %s feed with URL %s" % (feed_name, feed_uri))
1360
1361 config_file.write("src/gz %s %s\n" % (feed_name, feed_uri))
1362
1363 """
1364 Allow to use package deploy directory contents as quick devel-testing
1365 feed. This creates individual feed configs for each arch subdir of those
1366 specified as compatible for the current machine.
1367 NOTE: Development-helper feature, NOT a full-fledged feed.
1368 """
1369 if (self.d.getVar('FEED_DEPLOYDIR_BASE_URI', True) or "") != "":
1370 for arch in self.pkg_archs.split():
1371 cfg_file_name = os.path.join(self.target_rootfs,
1372 self.d.getVar("sysconfdir", True),
1373 "opkg",
1374 "local-%s-feed.conf" % arch)
1375
1376 with open(cfg_file_name, "w+") as cfg_file:
1377 cfg_file.write("src/gz local-%s %s/%s" %
1378 (arch,
1379 self.d.getVar('FEED_DEPLOYDIR_BASE_URI', True),
1380 arch))
1381
1382 def _create_config(self):
1383 with open(self.config_file, "w+") as config_file:
1384 priority = 1
1385 for arch in self.pkg_archs.split():
1386 config_file.write("arch %s %d\n" % (arch, priority))
1387 priority += 5
1388
1389 config_file.write("src oe file:%s\n" % self.deploy_dir)
1390
1391 for arch in self.pkg_archs.split():
1392 pkgs_dir = os.path.join(self.deploy_dir, arch)
1393 if os.path.isdir(pkgs_dir):
1394 config_file.write("src oe-%s file:%s\n" %
1395 (arch, pkgs_dir))
1396
1397 def insert_feeds_uris(self):
1398 if self.feed_uris == "":
1399 return
1400
1401 rootfs_config = os.path.join('%s/etc/opkg/base-feeds.conf'
1402 % self.target_rootfs)
1403
1404 with open(rootfs_config, "w+") as config_file:
1405 uri_iterator = 0
1406 for uri in self.feed_uris.split():
1407 full_uri = uri
1408 if self.feed_prefix:
1409 full_uri = os.path.join(uri, self.feed_prefix)
1410
1411 for arch in self.pkg_archs.split():
1412 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1413 continue
1414 bb.note('Note: adding opkg feed url-%s-%d (%s)' %
1415 (arch, uri_iterator, full_uri))
1416
1417 config_file.write("src/gz uri-%s-%d %s/%s\n" %
1418 (arch, uri_iterator, full_uri, arch))
1419 uri_iterator += 1
1420
1421 def update(self):
1422 self.deploy_dir_lock()
1423
1424 cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args)
1425
1426 try:
1427 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1428 except subprocess.CalledProcessError as e:
1429 self.deploy_dir_unlock()
1430 bb.fatal("Unable to update the package index files. Command '%s' "
1431 "returned %d:\n%s" % (cmd, e.returncode, e.output))
1432
1433 self.deploy_dir_unlock()
1434
1435 def install(self, pkgs, attempt_only=False):
1436 if attempt_only and len(pkgs) == 0:
1437 return
1438
1439 cmd = "%s %s install %s" % (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1440
1441 os.environ['D'] = self.target_rootfs
1442 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1443 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1444 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
1445 os.environ['INTERCEPT_DIR'] = os.path.join(self.d.getVar('WORKDIR', True),
1446 "intercept_scripts")
1447 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE', True)
1448
1449 try:
1450 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1451 bb.note(cmd)
1452 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1453 bb.note(output)
1454 except subprocess.CalledProcessError as e:
1455 (bb.fatal, bb.note)[attempt_only]("Unable to install packages. "
1456 "Command '%s' returned %d:\n%s" %
1457 (cmd, e.returncode, e.output))
1458
1459 def remove(self, pkgs, with_dependencies=True):
1460 if with_dependencies:
1461 cmd = "%s %s --force-depends --force-remove --force-removal-of-dependent-packages remove %s" % \
1462 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1463 else:
1464 cmd = "%s %s --force-depends remove %s" % \
1465 (self.opkg_cmd, self.opkg_args, ' '.join(pkgs))
1466
1467 try:
1468 bb.note(cmd)
1469 output = subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1470 bb.note(output)
1471 except subprocess.CalledProcessError as e:
1472 bb.fatal("Unable to remove packages. Command '%s' "
1473 "returned %d:\n%s" % (e.cmd, e.returncode, e.output))
1474
1475 def write_index(self):
1476 self.deploy_dir_lock()
1477
1478 result = self.indexer.write_index()
1479
1480 self.deploy_dir_unlock()
1481
1482 if result is not None:
1483 bb.fatal(result)
1484
1485 def remove_packaging_data(self):
1486 bb.utils.remove(self.opkg_dir, True)
1487 # create the directory back, it's needed by PM lock
1488 bb.utils.mkdirhier(self.opkg_dir)
1489
1490 def list_installed(self, format=None):
1491 return OpkgPkgsList(self.d, self.target_rootfs, self.config_file).list(format)
1492
1493 def handle_bad_recommendations(self):
1494 bad_recommendations = self.d.getVar("BAD_RECOMMENDATIONS", True) or ""
1495 if bad_recommendations.strip() == "":
1496 return
1497
1498 status_file = os.path.join(self.opkg_dir, "status")
1499
1500 # If status file existed, it means the bad recommendations has already
1501 # been handled
1502 if os.path.exists(status_file):
1503 return
1504
1505 cmd = "%s %s info " % (self.opkg_cmd, self.opkg_args)
1506
1507 with open(status_file, "w+") as status:
1508 for pkg in bad_recommendations.split():
1509 pkg_info = cmd + pkg
1510
1511 try:
1512 output = subprocess.check_output(pkg_info.split(), stderr=subprocess.STDOUT).strip()
1513 except subprocess.CalledProcessError as e:
1514 bb.fatal("Cannot get package info. Command '%s' "
1515 "returned %d:\n%s" % (pkg_info, e.returncode, e.output))
1516
1517 if output == "":
1518 bb.note("Ignored bad recommendation: '%s' is "
1519 "not a package" % pkg)
1520 continue
1521
1522 for line in output.split('\n'):
1523 if line.startswith("Status:"):
1524 status.write("Status: deinstall hold not-installed\n")
1525 else:
1526 status.write(line + "\n")
1527
1528 # Append a blank line after each package entry to ensure that it
1529 # is separated from the following entry
1530 status.write("\n")
1531
1532 '''
1533 The following function dummy installs pkgs and returns the log of output.
1534 '''
1535 def dummy_install(self, pkgs):
1536 if len(pkgs) == 0:
1537 return
1538
1539 # Create an temp dir as opkg root for dummy installation
1540 temp_rootfs = self.d.expand('${T}/opkg')
1541 temp_opkg_dir = os.path.join(temp_rootfs, 'var/lib/opkg')
1542 bb.utils.mkdirhier(temp_opkg_dir)
1543
1544 opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs)
1545 opkg_args += self.d.getVar("OPKG_ARGS", True)
1546
1547 cmd = "%s %s update" % (self.opkg_cmd, opkg_args)
1548 try:
1549 subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1550 except subprocess.CalledProcessError as e:
1551 bb.fatal("Unable to update. Command '%s' "
1552 "returned %d:\n%s" % (cmd, e.returncode, e.output))
1553
1554 # Dummy installation
1555 cmd = "%s %s --noaction install %s " % (self.opkg_cmd,
1556 opkg_args,
1557 ' '.join(pkgs))
1558 try:
1559 output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
1560 except subprocess.CalledProcessError as e:
1561 bb.fatal("Unable to dummy install packages. Command '%s' "
1562 "returned %d:\n%s" % (cmd, e.returncode, e.output))
1563
1564 bb.utils.remove(temp_rootfs, True)
1565
1566 return output
1567
1568 def backup_packaging_data(self):
1569 # Save the opkglib for increment ipk image generation
1570 if os.path.exists(self.saved_opkg_dir):
1571 bb.utils.remove(self.saved_opkg_dir, True)
1572 shutil.copytree(self.opkg_dir,
1573 self.saved_opkg_dir,
1574 symlinks=True)
1575
1576 def recover_packaging_data(self):
1577 # Move the opkglib back
1578 if os.path.exists(self.saved_opkg_dir):
1579 if os.path.exists(self.opkg_dir):
1580 bb.utils.remove(self.opkg_dir, True)
1581
1582 bb.note('Recover packaging data')
1583 shutil.copytree(self.saved_opkg_dir,
1584 self.opkg_dir,
1585 symlinks=True)
1586
1587
1588class DpkgPM(PackageManager):
1589 def __init__(self, d, target_rootfs, archs, base_archs, apt_conf_dir=None):
1590 super(DpkgPM, self).__init__(d)
1591 self.target_rootfs = target_rootfs
1592 self.deploy_dir = self.d.getVar('DEPLOY_DIR_DEB', True)
1593 if apt_conf_dir is None:
1594 self.apt_conf_dir = self.d.expand("${APTCONF_TARGET}/apt")
1595 else:
1596 self.apt_conf_dir = apt_conf_dir
1597 self.apt_conf_file = os.path.join(self.apt_conf_dir, "apt.conf")
1598 self.apt_get_cmd = bb.utils.which(os.getenv('PATH'), "apt-get")
1599
1600 self.apt_args = d.getVar("APT_ARGS", True)
1601
1602 self.all_arch_list = archs.split()
1603 all_mlb_pkg_arch_list = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS', True) or "").replace('-', '_').split()
1604 self.all_arch_list.extend(arch for arch in all_mlb_pkg_arch_list if arch not in self.all_arch_list)
1605
1606 self._create_configs(archs, base_archs)
1607
1608 self.indexer = DpkgIndexer(self.d, self.deploy_dir)
1609
1610 """
1611 This function will change a package's status in /var/lib/dpkg/status file.
1612 If 'packages' is None then the new_status will be applied to all
1613 packages
1614 """
1615 def mark_packages(self, status_tag, packages=None):
1616 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1617
1618 with open(status_file, "r") as sf:
1619 with open(status_file + ".tmp", "w+") as tmp_sf:
1620 if packages is None:
1621 tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)",
1622 r"Package: \1\n\2Status: \3%s" % status_tag,
1623 sf.read()))
1624 else:
1625 if type(packages).__name__ != "list":
1626 raise TypeError("'packages' should be a list object")
1627
1628 status = sf.read()
1629 for pkg in packages:
1630 status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg,
1631 r"Package: %s\n\1Status: \2%s" % (pkg, status_tag),
1632 status)
1633
1634 tmp_sf.write(status)
1635
1636 os.rename(status_file + ".tmp", status_file)
1637
1638 """
1639 Run the pre/post installs for package "package_name". If package_name is
1640 None, then run all pre/post install scriptlets.
1641 """
1642 def run_pre_post_installs(self, package_name=None):
1643 info_dir = self.target_rootfs + "/var/lib/dpkg/info"
1644 suffixes = [(".preinst", "Preinstall"), (".postinst", "Postinstall")]
1645 status_file = self.target_rootfs + "/var/lib/dpkg/status"
1646 installed_pkgs = []
1647
1648 with open(status_file, "r") as status:
1649 for line in status.read().split('\n'):
1650 m = re.match("^Package: (.*)", line)
1651 if m is not None:
1652 installed_pkgs.append(m.group(1))
1653
1654 if package_name is not None and not package_name in installed_pkgs:
1655 return
1656
1657 os.environ['D'] = self.target_rootfs
1658 os.environ['OFFLINE_ROOT'] = self.target_rootfs
1659 os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs
1660 os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs
1661 os.environ['INTERCEPT_DIR'] = os.path.join(self.d.getVar('WORKDIR', True),
1662 "intercept_scripts")
1663 os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE', True)
1664
1665 failed_pkgs = []
1666 for pkg_name in installed_pkgs:
1667 for suffix in suffixes:
1668 p_full = os.path.join(info_dir, pkg_name + suffix[0])
1669 if os.path.exists(p_full):
1670 try:
1671 bb.note("Executing %s for package: %s ..." %
1672 (suffix[1].lower(), pkg_name))
1673 subprocess.check_output(p_full, stderr=subprocess.STDOUT)
1674 except subprocess.CalledProcessError as e:
1675 bb.note("%s for package %s failed with %d:\n%s" %
1676 (suffix[1], pkg_name, e.returncode, e.output))
1677 failed_pkgs.append(pkg_name)
1678 break
1679
1680 if len(failed_pkgs):
1681 self.mark_packages("unpacked", failed_pkgs)
1682
1683 def update(self):
1684 os.environ['APT_CONFIG'] = self.apt_conf_file
1685
1686 self.deploy_dir_lock()
1687
1688 cmd = "%s update" % self.apt_get_cmd
1689
1690 try:
1691 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1692 except subprocess.CalledProcessError as e:
1693 bb.fatal("Unable to update the package index files. Command '%s' "
1694 "returned %d:\n%s" % (e.cmd, e.returncode, e.output))
1695
1696 self.deploy_dir_unlock()
1697
1698 def install(self, pkgs, attempt_only=False):
1699 if attempt_only and len(pkgs) == 0:
1700 return
1701
1702 os.environ['APT_CONFIG'] = self.apt_conf_file
1703
1704 cmd = "%s %s install --force-yes --allow-unauthenticated %s" % \
1705 (self.apt_get_cmd, self.apt_args, ' '.join(pkgs))
1706
1707 try:
1708 bb.note("Installing the following packages: %s" % ' '.join(pkgs))
1709 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1710 except subprocess.CalledProcessError as e:
1711 (bb.fatal, bb.note)[attempt_only]("Unable to install packages. "
1712 "Command '%s' returned %d:\n%s" %
1713 (cmd, e.returncode, e.output))
1714
1715 # rename *.dpkg-new files/dirs
1716 for root, dirs, files in os.walk(self.target_rootfs):
1717 for dir in dirs:
1718 new_dir = re.sub("\.dpkg-new", "", dir)
1719 if dir != new_dir:
1720 os.rename(os.path.join(root, dir),
1721 os.path.join(root, new_dir))
1722
1723 for file in files:
1724 new_file = re.sub("\.dpkg-new", "", file)
1725 if file != new_file:
1726 os.rename(os.path.join(root, file),
1727 os.path.join(root, new_file))
1728
1729
1730 def remove(self, pkgs, with_dependencies=True):
1731 if with_dependencies:
1732 os.environ['APT_CONFIG'] = self.apt_conf_file
1733 cmd = "%s purge %s" % (self.apt_get_cmd, ' '.join(pkgs))
1734 else:
1735 cmd = "%s --admindir=%s/var/lib/dpkg --instdir=%s" \
1736 " -P --force-depends %s" % \
1737 (bb.utils.which(os.getenv('PATH'), "dpkg"),
1738 self.target_rootfs, self.target_rootfs, ' '.join(pkgs))
1739
1740 try:
1741 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1742 except subprocess.CalledProcessError as e:
1743 bb.fatal("Unable to remove packages. Command '%s' "
1744 "returned %d:\n%s" % (e.cmd, e.returncode, e.output))
1745
1746 def write_index(self):
1747 self.deploy_dir_lock()
1748
1749 result = self.indexer.write_index()
1750
1751 self.deploy_dir_unlock()
1752
1753 if result is not None:
1754 bb.fatal(result)
1755
1756 def insert_feeds_uris(self):
1757 if self.feed_uris == "":
1758 return
1759
1760 sources_conf = os.path.join("%s/etc/apt/sources.list"
1761 % self.target_rootfs)
1762 arch_list = []
1763
1764 for arch in self.all_arch_list:
1765 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1766 continue
1767 arch_list.append(arch)
1768
1769 with open(sources_conf, "w+") as sources_file:
1770 for uri in self.feed_uris.split():
1771 full_uri = uri
1772 if self.feed_prefix:
1773 full_uri = os.path.join(uri, self.feed_prefix)
1774 for arch in arch_list:
1775 bb.note('Note: adding dpkg channel at (%s)' % uri)
1776 sources_file.write("deb %s/%s ./\n" %
1777 (full_uri, arch))
1778
1779 def _create_configs(self, archs, base_archs):
1780 base_archs = re.sub("_", "-", base_archs)
1781
1782 if os.path.exists(self.apt_conf_dir):
1783 bb.utils.remove(self.apt_conf_dir, True)
1784
1785 bb.utils.mkdirhier(self.apt_conf_dir)
1786 bb.utils.mkdirhier(self.apt_conf_dir + "/lists/partial/")
1787 bb.utils.mkdirhier(self.apt_conf_dir + "/apt.conf.d/")
1788
1789 arch_list = []
1790 for arch in self.all_arch_list:
1791 if not os.path.exists(os.path.join(self.deploy_dir, arch)):
1792 continue
1793 arch_list.append(arch)
1794
1795 with open(os.path.join(self.apt_conf_dir, "preferences"), "w+") as prefs_file:
1796 priority = 801
1797 for arch in arch_list:
1798 prefs_file.write(
1799 "Package: *\n"
1800 "Pin: release l=%s\n"
1801 "Pin-Priority: %d\n\n" % (arch, priority))
1802
1803 priority += 5
1804
1805 pkg_exclude = self.d.getVar('PACKAGE_EXCLUDE', True) or ""
1806 for pkg in pkg_exclude.split():
1807 prefs_file.write(
1808 "Package: %s\n"
1809 "Pin: release *\n"
1810 "Pin-Priority: -1\n\n" % pkg)
1811
1812 arch_list.reverse()
1813
1814 with open(os.path.join(self.apt_conf_dir, "sources.list"), "w+") as sources_file:
1815 for arch in arch_list:
1816 sources_file.write("deb file:%s/ ./\n" %
1817 os.path.join(self.deploy_dir, arch))
1818
1819 base_arch_list = base_archs.split()
1820 multilib_variants = self.d.getVar("MULTILIB_VARIANTS", True);
1821 for variant in multilib_variants.split():
1822 if variant == "lib32":
1823 base_arch_list.append("i386")
1824 elif variant == "lib64":
1825 base_arch_list.append("amd64")
1826
1827 with open(self.apt_conf_file, "w+") as apt_conf:
1828 with open(self.d.expand("${STAGING_ETCDIR_NATIVE}/apt/apt.conf.sample")) as apt_conf_sample:
1829 for line in apt_conf_sample.read().split("\n"):
1830 match_arch = re.match(" Architecture \".*\";$", line)
1831 architectures = ""
1832 if match_arch:
1833 for base_arch in base_arch_list:
1834 architectures += "\"%s\";" % base_arch
1835 apt_conf.write(" Architectures {%s};\n" % architectures);
1836 apt_conf.write(" Architecture \"%s\";\n" % base_archs)
1837 else:
1838 line = re.sub("#ROOTFS#", self.target_rootfs, line)
1839 line = re.sub("#APTCONF#", self.apt_conf_dir, line)
1840 apt_conf.write(line + "\n")
1841
1842 target_dpkg_dir = "%s/var/lib/dpkg" % self.target_rootfs
1843 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "info"))
1844
1845 bb.utils.mkdirhier(os.path.join(target_dpkg_dir, "updates"))
1846
1847 if not os.path.exists(os.path.join(target_dpkg_dir, "status")):
1848 open(os.path.join(target_dpkg_dir, "status"), "w+").close()
1849 if not os.path.exists(os.path.join(target_dpkg_dir, "available")):
1850 open(os.path.join(target_dpkg_dir, "available"), "w+").close()
1851
1852 def remove_packaging_data(self):
1853 bb.utils.remove(os.path.join(self.target_rootfs,
1854 self.d.getVar('opkglibdir', True)), True)
1855 bb.utils.remove(self.target_rootfs + "/var/lib/dpkg/", True)
1856
1857 def fix_broken_dependencies(self):
1858 os.environ['APT_CONFIG'] = self.apt_conf_file
1859
1860 cmd = "%s %s -f install" % (self.apt_get_cmd, self.apt_args)
1861
1862 try:
1863 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
1864 except subprocess.CalledProcessError as e:
1865 bb.fatal("Cannot fix broken dependencies. Command '%s' "
1866 "returned %d:\n%s" % (cmd, e.returncode, e.output))
1867
1868 def list_installed(self, format=None):
1869 return DpkgPkgsList(self.d, self.target_rootfs).list()
1870
1871
1872def generate_index_files(d):
1873 classes = d.getVar('PACKAGE_CLASSES', True).replace("package_", "").split()
1874
1875 indexer_map = {
1876 "rpm": (RpmIndexer, d.getVar('DEPLOY_DIR_RPM', True)),
1877 "ipk": (OpkgIndexer, d.getVar('DEPLOY_DIR_IPK', True)),
1878 "deb": (DpkgIndexer, d.getVar('DEPLOY_DIR_DEB', True))
1879 }
1880
1881 result = None
1882
1883 for pkg_class in classes:
1884 if not pkg_class in indexer_map:
1885 continue
1886
1887 if os.path.exists(indexer_map[pkg_class][1]):
1888 result = indexer_map[pkg_class][0](d, indexer_map[pkg_class][1]).write_index()
1889
1890 if result is not None:
1891 bb.fatal(result)
1892
1893if __name__ == "__main__":
1894 """
1895 We should be able to run this as a standalone script, from outside bitbake
1896 environment.
1897 """
1898 """
1899 TBD
1900 """