diff options
| author | Alexander Kanavin <alexander.kanavin@linux.intel.com> | 2017-02-13 16:44:48 +0200 |
|---|---|---|
| committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2017-03-14 14:42:17 +0000 |
| commit | 67615e01751bdba4e2186c86c44bebd9ded5233b (patch) | |
| tree | a0472e225aaa55eaadb10f419867fae3fcb31d0e /meta | |
| parent | d4efcded26706f50f8ca98d76df2b349ed1f1792 (diff) | |
| download | poky-67615e01751bdba4e2186c86c44bebd9ded5233b.tar.gz | |
rootfs_rpm.bbclass: migrate image creation to dnf
To properly look at this patch, you probably need a side-by-side diff viewing tool.
(From OE-Core rev: 65581c68d130fa74d703f6c3c92560e053857ac7)
Signed-off-by: Alexander Kanavin <alexander.kanavin@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta')
| -rw-r--r-- | meta/classes/rootfs_rpm.bbclass | 21 | ||||
| -rw-r--r-- | meta/lib/oe/package_manager.py | 1192 | ||||
| -rw-r--r-- | meta/lib/oe/rootfs.py | 18 | ||||
| -rw-r--r-- | meta/lib/oe/sdk.py | 7 |
4 files changed, 239 insertions, 999 deletions
diff --git a/meta/classes/rootfs_rpm.bbclass b/meta/classes/rootfs_rpm.bbclass index b8ff4cb7b6..65881a60a7 100644 --- a/meta/classes/rootfs_rpm.bbclass +++ b/meta/classes/rootfs_rpm.bbclass | |||
| @@ -2,20 +2,23 @@ | |||
| 2 | # Creates a root filesystem out of rpm packages | 2 | # Creates a root filesystem out of rpm packages |
| 3 | # | 3 | # |
| 4 | 4 | ||
| 5 | ROOTFS_PKGMANAGE = "rpm smartpm" | 5 | ROOTFS_PKGMANAGE = "rpm dnf" |
| 6 | ROOTFS_PKGMANAGE_BOOTSTRAP = "run-postinsts" | 6 | ROOTFS_PKGMANAGE_BOOTSTRAP = "run-postinsts" |
| 7 | 7 | ||
| 8 | # Add 100Meg of extra space for Smart | 8 | # dnf is using our custom distutils, and so will fail without these |
| 9 | IMAGE_ROOTFS_EXTRA_SPACE_append = "${@bb.utils.contains("PACKAGE_INSTALL", "smartpm", " + 102400", "" ,d)}" | 9 | export STAGING_INCDIR |
| 10 | export STAGING_LIBDIR | ||
| 10 | 11 | ||
| 11 | # Smart is python based, so be sure python-native is available to us. | 12 | # Add 100Meg of extra space for dnf |
| 13 | IMAGE_ROOTFS_EXTRA_SPACE_append = "${@bb.utils.contains("PACKAGE_INSTALL", "dnf", " + 102400", "" ,d)}" | ||
| 14 | |||
| 15 | # Dnf is python based, so be sure python-native is available to us. | ||
| 12 | EXTRANATIVEPATH += "python-native" | 16 | EXTRANATIVEPATH += "python-native" |
| 13 | 17 | ||
| 14 | # opkg is needed for update-alternatives | 18 | # opkg is needed for update-alternatives |
| 15 | RPMROOTFSDEPENDS = "rpm-native:do_populate_sysroot \ | 19 | RPMROOTFSDEPENDS = "rpm-native:do_populate_sysroot \ |
| 16 | rpmresolve-native:do_populate_sysroot \ | 20 | dnf-native:do_populate_sysroot \ |
| 17 | python-smartpm-native:do_populate_sysroot \ | 21 | createrepo-c-native:do_populate_sysroot \ |
| 18 | createrepo-native:do_populate_sysroot \ | ||
| 19 | opkg-native:do_populate_sysroot" | 22 | opkg-native:do_populate_sysroot" |
| 20 | 23 | ||
| 21 | do_rootfs[depends] += "${RPMROOTFSDEPENDS}" | 24 | do_rootfs[depends] += "${RPMROOTFSDEPENDS}" |
| @@ -35,7 +38,3 @@ python () { | |||
| 35 | d.setVar('RPM_POSTPROCESS_COMMANDS', '') | 38 | d.setVar('RPM_POSTPROCESS_COMMANDS', '') |
| 36 | 39 | ||
| 37 | } | 40 | } |
| 38 | # Smart is python based, so be sure python-native is available to us. | ||
| 39 | EXTRANATIVEPATH += "python-native" | ||
| 40 | |||
| 41 | rpmlibdir = "/var/lib/rpm" | ||
diff --git a/meta/lib/oe/package_manager.py b/meta/lib/oe/package_manager.py index 725997ce33..d51609189d 100644 --- a/meta/lib/oe/package_manager.py +++ b/meta/lib/oe/package_manager.py | |||
| @@ -102,109 +102,14 @@ class Indexer(object, metaclass=ABCMeta): | |||
| 102 | 102 | ||
| 103 | 103 | ||
| 104 | class RpmIndexer(Indexer): | 104 | class RpmIndexer(Indexer): |
| 105 | def get_ml_prefix_and_os_list(self, arch_var=None, os_var=None): | ||
| 106 | package_archs = collections.OrderedDict() | ||
| 107 | target_os = collections.OrderedDict() | ||
| 108 | |||
| 109 | if arch_var is not None and os_var is not None: | ||
| 110 | package_archs['default'] = self.d.getVar(arch_var).split() | ||
| 111 | package_archs['default'].reverse() | ||
| 112 | target_os['default'] = self.d.getVar(os_var).strip() | ||
| 113 | else: | ||
| 114 | package_archs['default'] = self.d.getVar("PACKAGE_ARCHS").split() | ||
| 115 | # arch order is reversed. This ensures the -best- match is | ||
| 116 | # listed first! | ||
| 117 | package_archs['default'].reverse() | ||
| 118 | target_os['default'] = self.d.getVar("TARGET_OS").strip() | ||
| 119 | multilibs = self.d.getVar('MULTILIBS') or "" | ||
| 120 | for ext in multilibs.split(): | ||
| 121 | eext = ext.split(':') | ||
| 122 | if len(eext) > 1 and eext[0] == 'multilib': | ||
| 123 | localdata = bb.data.createCopy(self.d) | ||
| 124 | default_tune_key = "DEFAULTTUNE_virtclass-multilib-" + eext[1] | ||
| 125 | default_tune = localdata.getVar(default_tune_key, False) | ||
| 126 | if default_tune is None: | ||
| 127 | default_tune_key = "DEFAULTTUNE_ML_" + eext[1] | ||
| 128 | default_tune = localdata.getVar(default_tune_key, False) | ||
| 129 | if default_tune: | ||
| 130 | localdata.setVar("DEFAULTTUNE", default_tune) | ||
| 131 | package_archs[eext[1]] = localdata.getVar('PACKAGE_ARCHS').split() | ||
| 132 | package_archs[eext[1]].reverse() | ||
| 133 | target_os[eext[1]] = localdata.getVar("TARGET_OS").strip() | ||
| 134 | |||
| 135 | ml_prefix_list = collections.OrderedDict() | ||
| 136 | for mlib in package_archs: | ||
| 137 | if mlib == 'default': | ||
| 138 | ml_prefix_list[mlib] = package_archs[mlib] | ||
| 139 | else: | ||
| 140 | ml_prefix_list[mlib] = list() | ||
| 141 | for arch in package_archs[mlib]: | ||
| 142 | if arch in ['all', 'noarch', 'any']: | ||
| 143 | ml_prefix_list[mlib].append(arch) | ||
| 144 | else: | ||
| 145 | ml_prefix_list[mlib].append(mlib + "_" + arch) | ||
| 146 | |||
| 147 | return (ml_prefix_list, target_os) | ||
| 148 | |||
| 149 | def write_index(self): | 105 | def write_index(self): |
| 150 | sdk_pkg_archs = (self.d.getVar('SDK_PACKAGE_ARCHS') or "").replace('-', '_').split() | ||
| 151 | all_mlb_pkg_archs = (self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS') or "").replace('-', '_').split() | ||
| 152 | |||
| 153 | mlb_prefix_list = self.get_ml_prefix_and_os_list()[0] | ||
| 154 | |||
| 155 | archs = set() | ||
| 156 | for item in mlb_prefix_list: | ||
| 157 | archs = archs.union(set(i.replace('-', '_') for i in mlb_prefix_list[item])) | ||
| 158 | |||
| 159 | if len(archs) == 0: | ||
| 160 | archs = archs.union(set(all_mlb_pkg_archs)) | ||
| 161 | |||
| 162 | archs = archs.union(set(sdk_pkg_archs)) | ||
| 163 | |||
| 164 | rpm_createrepo = bb.utils.which(os.environ['PATH'], "createrepo") | ||
| 165 | if not rpm_createrepo: | ||
| 166 | bb.error("Cannot rebuild index as createrepo was not found in %s" % os.environ['PATH']) | ||
| 167 | return | ||
| 168 | |||
| 169 | if self.d.getVar('PACKAGE_FEED_SIGN') == '1': | 106 | if self.d.getVar('PACKAGE_FEED_SIGN') == '1': |
| 170 | signer = get_signer(self.d, self.d.getVar('PACKAGE_FEED_GPG_BACKEND')) | 107 | raise NotImplementedError('Package feed signing not yet implementd for rpm') |
| 171 | else: | ||
| 172 | signer = None | ||
| 173 | index_cmds = [] | ||
| 174 | repomd_files = [] | ||
| 175 | rpm_dirs_found = False | ||
| 176 | for arch in archs: | ||
| 177 | dbpath = os.path.join(self.d.getVar('WORKDIR'), 'rpmdb', arch) | ||
| 178 | if os.path.exists(dbpath): | ||
| 179 | bb.utils.remove(dbpath, True) | ||
| 180 | arch_dir = os.path.join(self.deploy_dir, arch) | ||
| 181 | if not os.path.isdir(arch_dir): | ||
| 182 | continue | ||
| 183 | |||
| 184 | index_cmds.append("%s --dbpath %s --update -q %s" % \ | ||
| 185 | (rpm_createrepo, dbpath, arch_dir)) | ||
| 186 | repomd_files.append(os.path.join(arch_dir, 'repodata', 'repomd.xml')) | ||
| 187 | |||
| 188 | rpm_dirs_found = True | ||
| 189 | |||
| 190 | if not rpm_dirs_found: | ||
| 191 | bb.note("There are no packages in %s" % self.deploy_dir) | ||
| 192 | return | ||
| 193 | 108 | ||
| 194 | # Create repodata | 109 | createrepo_c = bb.utils.which(os.environ['PATH'], "createrepo_c") |
| 195 | result = oe.utils.multiprocess_exec(index_cmds, create_index) | 110 | result = create_index("%s --update -q %s" % (createrepo_c, self.deploy_dir)) |
| 196 | if result: | 111 | if result: |
| 197 | bb.fatal('%s' % ('\n'.join(result))) | 112 | bb.fatal(result) |
| 198 | # Sign repomd | ||
| 199 | if signer: | ||
| 200 | for repomd in repomd_files: | ||
| 201 | feed_sig_type = self.d.getVar('PACKAGE_FEED_GPG_SIGNATURE_TYPE') | ||
| 202 | is_ascii_sig = (feed_sig_type.upper() != "BIN") | ||
| 203 | signer.detach_sign(repomd, | ||
| 204 | self.d.getVar('PACKAGE_FEED_GPG_NAME'), | ||
| 205 | self.d.getVar('PACKAGE_FEED_GPG_PASSPHRASE_FILE'), | ||
| 206 | armor=is_ascii_sig) | ||
| 207 | |||
| 208 | 113 | ||
| 209 | class OpkgIndexer(Indexer): | 114 | class OpkgIndexer(Indexer): |
| 210 | def write_index(self): | 115 | def write_index(self): |
| @@ -347,117 +252,9 @@ class PkgsList(object, metaclass=ABCMeta): | |||
| 347 | def list_pkgs(self): | 252 | def list_pkgs(self): |
| 348 | pass | 253 | pass |
| 349 | 254 | ||
| 350 | |||
| 351 | class RpmPkgsList(PkgsList): | 255 | class RpmPkgsList(PkgsList): |
| 352 | def __init__(self, d, rootfs_dir, arch_var=None, os_var=None): | ||
| 353 | super(RpmPkgsList, self).__init__(d, rootfs_dir) | ||
| 354 | |||
| 355 | self.rpm_cmd = bb.utils.which(os.getenv('PATH'), "rpm") | ||
| 356 | self.image_rpmlib = os.path.join(self.rootfs_dir, 'var/lib/rpm') | ||
| 357 | |||
| 358 | self.ml_prefix_list, self.ml_os_list = \ | ||
| 359 | RpmIndexer(d, rootfs_dir).get_ml_prefix_and_os_list(arch_var, os_var) | ||
| 360 | |||
| 361 | # Determine rpm version | ||
| 362 | try: | ||
| 363 | output = subprocess.check_output([self.rpm_cmd, "--version"], stderr=subprocess.STDOUT).decode("utf-8") | ||
| 364 | except subprocess.CalledProcessError as e: | ||
| 365 | bb.fatal("Getting rpm version failed. Command '%s' " | ||
| 366 | "returned %d:\n%s" % (self.rpm_cmd, e.returncode, e.output.decode("utf-8"))) | ||
| 367 | |||
| 368 | ''' | ||
| 369 | Translate the RPM/Smart format names to the OE multilib format names | ||
| 370 | ''' | ||
| 371 | def _pkg_translate_smart_to_oe(self, pkg, arch): | ||
| 372 | new_pkg = pkg | ||
| 373 | new_arch = arch | ||
| 374 | fixed_arch = arch.replace('_', '-') | ||
| 375 | found = 0 | ||
| 376 | for mlib in self.ml_prefix_list: | ||
| 377 | for cmp_arch in self.ml_prefix_list[mlib]: | ||
| 378 | fixed_cmp_arch = cmp_arch.replace('_', '-') | ||
| 379 | if fixed_arch == fixed_cmp_arch: | ||
| 380 | if mlib == 'default': | ||
| 381 | new_pkg = pkg | ||
| 382 | new_arch = cmp_arch | ||
| 383 | else: | ||
| 384 | new_pkg = mlib + '-' + pkg | ||
| 385 | # We need to strip off the ${mlib}_ prefix on the arch | ||
| 386 | new_arch = cmp_arch.replace(mlib + '_', '') | ||
| 387 | |||
| 388 | # Workaround for bug 3565. Simply look to see if we | ||
| 389 | # know of a package with that name, if not try again! | ||
| 390 | filename = os.path.join(self.d.getVar('PKGDATA_DIR'), | ||
| 391 | 'runtime-reverse', | ||
| 392 | new_pkg) | ||
| 393 | if os.path.exists(filename): | ||
| 394 | found = 1 | ||
| 395 | break | ||
| 396 | |||
| 397 | if found == 1 and fixed_arch == fixed_cmp_arch: | ||
| 398 | break | ||
| 399 | #bb.note('%s, %s -> %s, %s' % (pkg, arch, new_pkg, new_arch)) | ||
| 400 | return new_pkg, new_arch | ||
| 401 | |||
| 402 | def _list_pkg_deps(self): | ||
| 403 | cmd = [bb.utils.which(os.getenv('PATH'), "rpmresolve"), | ||
| 404 | "-t", self.image_rpmlib] | ||
| 405 | |||
| 406 | try: | ||
| 407 | output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip().decode("utf-8") | ||
| 408 | except subprocess.CalledProcessError as e: | ||
| 409 | bb.fatal("Cannot get the package dependencies. Command '%s' " | ||
| 410 | "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8"))) | ||
| 411 | |||
| 412 | return output | ||
| 413 | |||
| 414 | def list_pkgs(self): | 256 | def list_pkgs(self): |
| 415 | cmd = [self.rpm_cmd, '--root', self.rootfs_dir] | 257 | return RpmPM(self.d, self.rootfs_dir, self.d.getVar('TARGET_VENDOR')).list_installed() |
| 416 | cmd.extend(['-D', '_dbpath /var/lib/rpm']) | ||
| 417 | cmd.extend(['-qa', '--qf', '[%{NAME} %{ARCH} %{VERSION} %{PACKAGEORIGIN}\n]']) | ||
| 418 | |||
| 419 | try: | ||
| 420 | tmp_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).strip().decode("utf-8") | ||
| 421 | except subprocess.CalledProcessError as e: | ||
| 422 | bb.fatal("Cannot get the installed packages list. Command '%s' " | ||
| 423 | "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8"))) | ||
| 424 | |||
| 425 | output = dict() | ||
| 426 | deps = dict() | ||
| 427 | dependencies = self._list_pkg_deps() | ||
| 428 | |||
| 429 | # Populate deps dictionary for better manipulation | ||
| 430 | for line in dependencies.splitlines(): | ||
| 431 | try: | ||
| 432 | pkg, dep = line.split("|") | ||
| 433 | if not pkg in deps: | ||
| 434 | deps[pkg] = list() | ||
| 435 | if not dep in deps[pkg]: | ||
| 436 | deps[pkg].append(dep) | ||
| 437 | except: | ||
| 438 | # Ignore any other lines they're debug or errors | ||
| 439 | pass | ||
| 440 | |||
| 441 | for line in tmp_output.split('\n'): | ||
| 442 | if len(line.strip()) == 0: | ||
| 443 | continue | ||
| 444 | pkg = line.split()[0] | ||
| 445 | arch = line.split()[1] | ||
| 446 | ver = line.split()[2] | ||
| 447 | dep = deps.get(pkg, []) | ||
| 448 | |||
| 449 | # Skip GPG keys | ||
| 450 | if pkg == 'gpg-pubkey': | ||
| 451 | continue | ||
| 452 | |||
| 453 | pkgorigin = line.split()[3] | ||
| 454 | new_pkg, new_arch = self._pkg_translate_smart_to_oe(pkg, arch) | ||
| 455 | |||
| 456 | output[new_pkg] = {"arch":new_arch, "ver":ver, | ||
| 457 | "filename":pkgorigin, "deps":dep} | ||
| 458 | |||
| 459 | return output | ||
| 460 | |||
| 461 | 258 | ||
| 462 | class OpkgPkgsList(PkgsList): | 259 | class OpkgPkgsList(PkgsList): |
| 463 | def __init__(self, d, rootfs_dir, config_file): | 260 | def __init__(self, d, rootfs_dir, config_file): |
| @@ -553,6 +350,16 @@ class PackageManager(object, metaclass=ABCMeta): | |||
| 553 | pass | 350 | pass |
| 554 | 351 | ||
| 555 | """ | 352 | """ |
| 353 | Returns the path to a tmpdir where resides the contents of a package. | ||
| 354 | |||
| 355 | Deleting the tmpdir is responsability of the caller. | ||
| 356 | |||
| 357 | """ | ||
| 358 | @abstractmethod | ||
| 359 | def extract(self, pkg): | ||
| 360 | pass | ||
| 361 | |||
| 362 | """ | ||
| 556 | Add remote package feeds into repository manager configuration. The parameters | 363 | Add remote package feeds into repository manager configuration. The parameters |
| 557 | for the feeds are set by feed_uris, feed_base_paths and feed_archs. | 364 | for the feeds are set by feed_uris, feed_base_paths and feed_archs. |
| 558 | See http://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#var-PACKAGE_FEED_URIS | 365 | See http://www.yoctoproject.org/docs/current/ref-manual/ref-manual.html#var-PACKAGE_FEED_URIS |
| @@ -661,821 +468,264 @@ class RpmPM(PackageManager): | |||
| 661 | self.target_rootfs = target_rootfs | 468 | self.target_rootfs = target_rootfs |
| 662 | self.target_vendor = target_vendor | 469 | self.target_vendor = target_vendor |
| 663 | self.task_name = task_name | 470 | self.task_name = task_name |
| 664 | self.providename = providename | 471 | if arch_var == None: |
| 665 | self.fullpkglist = list() | 472 | self.archs = self.d.getVar('ALL_MULTILIB_PACKAGE_ARCHS').replace("-","_") |
| 666 | self.deploy_dir = self.d.getVar('DEPLOY_DIR_RPM') | 473 | else: |
| 667 | self.etcrpm_dir = os.path.join(self.target_rootfs, "etc/rpm") | 474 | self.archs = self.d.getVar(arch_var).replace("-","_") |
| 668 | self.install_dir_name = "oe_install" | 475 | if task_name == "host": |
| 669 | self.install_dir_path = os.path.join(self.target_rootfs, self.install_dir_name) | 476 | self.primary_arch = self.d.getVar('SDK_ARCH') |
| 670 | self.rpm_cmd = bb.utils.which(os.getenv('PATH'), "rpm") | 477 | else: |
| 671 | self.smart_cmd = bb.utils.which(os.getenv('PATH'), "smart") | 478 | self.primary_arch = self.d.getVar('MACHINE_ARCH') |
| 672 | # 0 = --log-level=warning, only warnings | 479 | |
| 673 | # 1 = --log-level=info (includes information about executing scriptlets and their output), default | 480 | self.rpm_repo_dir = oe.path.join(self.d.getVar('WORKDIR'), "oe-rootfs-repo") |
| 674 | # 2 = --log-level=debug | 481 | bb.utils.mkdirhier(self.rpm_repo_dir) |
| 675 | # 3 = --log-level=debug plus dumps of scriplet content and command invocation | 482 | oe.path.symlink(self.d.getVar('DEPLOY_DIR_RPM'), oe.path.join(self.rpm_repo_dir, "rpm"), True) |
| 676 | self.debug_level = int(d.getVar('ROOTFS_RPM_DEBUG') or "1") | 483 | |
| 677 | self.smart_opt = ["--log-level=%s" % | 484 | self.saved_packaging_data = self.d.expand('${T}/saved_packaging_data/%s' % self.task_name) |
| 678 | ("warning" if self.debug_level == 0 else | 485 | if not os.path.exists(self.d.expand('${T}/saved_packaging_data')): |
| 679 | "info" if self.debug_level == 1 else | 486 | bb.utils.mkdirhier(self.d.expand('${T}/saved_packaging_data')) |
| 680 | "debug"), "--data-dir=%s" % | 487 | self.packaging_data_dirs = ['var/lib/rpm', 'var/lib/dnf', 'var/cache/dnf'] |
| 681 | os.path.join(target_rootfs, 'var/lib/smart')] | ||
| 682 | self.scriptlet_wrapper = self.d.expand('${WORKDIR}/scriptlet_wrapper') | ||
| 683 | self.solution_manifest = self.d.expand('${T}/saved/%s_solution' % | 488 | self.solution_manifest = self.d.expand('${T}/saved/%s_solution' % |
| 684 | self.task_name) | 489 | self.task_name) |
| 685 | self.saved_rpmlib = self.d.expand('${T}/saved/%s' % self.task_name) | ||
| 686 | self.image_rpmlib = os.path.join(self.target_rootfs, 'var/lib/rpm') | ||
| 687 | |||
| 688 | if not os.path.exists(self.d.expand('${T}/saved')): | 490 | if not os.path.exists(self.d.expand('${T}/saved')): |
| 689 | bb.utils.mkdirhier(self.d.expand('${T}/saved')) | 491 | bb.utils.mkdirhier(self.d.expand('${T}/saved')) |
| 690 | 492 | ||
| 691 | packageindex_dir = os.path.join(self.d.getVar('WORKDIR'), 'rpms') | 493 | def _configure_dnf(self): |
| 692 | self.indexer = RpmIndexer(self.d, packageindex_dir) | 494 | # libsolv handles 'noarch' internally, we don't need to specify it explicitly |
| 693 | self.pkgs_list = RpmPkgsList(self.d, self.target_rootfs, arch_var, os_var) | 495 | archs = [i for i in 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 | archconfdir = "%s/%s" %(self.target_rootfs, "etc/dnf/vars/") | ||
| 500 | bb.utils.mkdirhier(archconfdir) | ||
| 501 | open(archconfdir + "arch", 'w').write(":".join(archs)) | ||
| 502 | |||
| 503 | open(oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"), 'w').write("") | ||
| 504 | |||
| 505 | |||
| 506 | def _configure_rpm(self): | ||
| 507 | # We need to configure rpm to use our primary package architecture as the installation architecture, | ||
| 508 | # and to make it compatible with other package architectures that we use. | ||
| 509 | # Otherwise it will refuse to proceed with packages installation. | ||
| 510 | platformconfdir = "%s/%s" %(self.target_rootfs, "etc/rpm/") | ||
| 511 | rpmrcconfdir = "%s/%s" %(self.target_rootfs, "etc/") | ||
| 512 | bb.utils.mkdirhier(platformconfdir) | ||
| 513 | open(platformconfdir + "platform", 'w').write("%s-pc-linux" % self.primary_arch) | ||
| 514 | open(rpmrcconfdir + "rpmrc", 'w').write("arch_compat: %s: %s\n" % (self.primary_arch, self.archs if len(self.archs) > 0 else self.primary_arch)) | ||
| 515 | |||
| 516 | open(platformconfdir + "macros", 'w').write("%_transaction_color 7\n") | ||
| 517 | if self.d.getVar('RPM_PREFER_ELF_ARCH'): | ||
| 518 | open(platformconfdir + "macros", 'a').write("%%_prefer_color %s" % (self.d.getVar('RPM_PREFER_ELF_ARCH'))) | ||
| 519 | else: | ||
| 520 | open(platformconfdir + "macros", 'a').write("%_prefer_color 7") | ||
| 521 | |||
| 522 | if self.d.getVar('RPM_SIGN_PACKAGES') == '1': | ||
| 523 | raise NotImplementedError("Signature verification with rpm not yet supported.") | ||
| 524 | |||
| 525 | def create_configs(self): | ||
| 526 | self._configure_dnf() | ||
| 527 | self._configure_rpm() | ||
| 694 | 528 | ||
| 695 | self.ml_prefix_list, self.ml_os_list = self.indexer.get_ml_prefix_and_os_list(arch_var, os_var) | 529 | def write_index(self): |
| 530 | lockfilename = self.d.getVar('DEPLOY_DIR_RPM') + "/rpm.lock" | ||
| 531 | lf = bb.utils.lockfile(lockfilename, False) | ||
| 532 | RpmIndexer(self.d, self.rpm_repo_dir).write_index() | ||
| 533 | bb.utils.unlockfile(lf) | ||
| 696 | 534 | ||
| 697 | def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs): | 535 | def insert_feeds_uris(self, feed_uris, feed_base_paths, feed_archs): |
| 698 | if feed_uris == "": | 536 | if feed_uris == "": |
| 699 | return | 537 | return |
| 700 | 538 | ||
| 701 | arch_list = [] | 539 | raise NotImplementedError("Adding remote dnf feeds not yet supported.") |
| 702 | if feed_archs is not None: | ||
| 703 | # User define feed architectures | ||
| 704 | arch_list = feed_archs.split() | ||
| 705 | else: | ||
| 706 | # List must be prefered to least preferred order | ||
| 707 | default_platform_extra = list() | ||
| 708 | platform_extra = list() | ||
| 709 | bbextendvariant = self.d.getVar('BBEXTENDVARIANT') or "" | ||
| 710 | for mlib in self.ml_os_list: | ||
| 711 | for arch in self.ml_prefix_list[mlib]: | ||
| 712 | plt = arch.replace('-', '_') + '-.*-' + self.ml_os_list[mlib] | ||
| 713 | if mlib == bbextendvariant: | ||
| 714 | if plt not in default_platform_extra: | ||
| 715 | default_platform_extra.append(plt) | ||
| 716 | else: | ||
| 717 | if plt not in platform_extra: | ||
| 718 | platform_extra.append(plt) | ||
| 719 | platform_extra = default_platform_extra + platform_extra | ||
| 720 | |||
| 721 | for canonical_arch in platform_extra: | ||
| 722 | arch = canonical_arch.split('-')[0] | ||
| 723 | if not os.path.exists(os.path.join(self.deploy_dir, arch)): | ||
| 724 | continue | ||
| 725 | arch_list.append(arch) | ||
| 726 | 540 | ||
| 727 | feed_uris = self.construct_uris(feed_uris.split(), feed_base_paths.split()) | 541 | def _prepare_pkg_transaction(self): |
| 542 | os.environ['D'] = self.target_rootfs | ||
| 543 | os.environ['OFFLINE_ROOT'] = self.target_rootfs | ||
| 544 | os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs | ||
| 545 | os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs | ||
| 546 | os.environ['INTERCEPT_DIR'] = oe.path.join(self.d.getVar('WORKDIR'), | ||
| 547 | "intercept_scripts") | ||
| 548 | os.environ['NATIVE_ROOT'] = self.d.getVar('STAGING_DIR_NATIVE') | ||
| 728 | 549 | ||
| 729 | uri_iterator = 0 | ||
| 730 | channel_priority = 10 + 5 * len(feed_uris) * (len(arch_list) if arch_list else 1) | ||
| 731 | |||
| 732 | for uri in feed_uris: | ||
| 733 | if arch_list: | ||
| 734 | for arch in arch_list: | ||
| 735 | bb.note('Adding Smart channel url%d%s (%s)' % | ||
| 736 | (uri_iterator, arch, channel_priority)) | ||
| 737 | self._invoke_smart(['channel', '--add', 'url%d-%s' % (uri_iterator, arch), | ||
| 738 | 'type=rpm-md', 'baseurl=%s/%s' % (uri, arch), '-y']) | ||
| 739 | self._invoke_smart(['channel', '--set', 'url%d-%s' % (uri_iterator, arch), | ||
| 740 | 'priority=%d' % channel_priority]) | ||
| 741 | channel_priority -= 5 | ||
| 742 | else: | ||
| 743 | bb.note('Adding Smart channel url%d (%s)' % | ||
| 744 | (uri_iterator, channel_priority)) | ||
| 745 | self._invoke_smart(['channel', '--add', 'url%d' % uri_iterator, | ||
| 746 | 'type=rpm-md', 'baseurl=%s' % uri, '-y']) | ||
| 747 | self._invoke_smart(['channel', '--set', 'url%d' % uri_iterator, | ||
| 748 | 'priority=%d' % channel_priority]) | ||
| 749 | channel_priority -= 5 | ||
| 750 | 550 | ||
| 751 | uri_iterator += 1 | 551 | def install(self, pkgs, attempt_only = False): |
| 552 | if len(pkgs) == 0: | ||
| 553 | return | ||
| 554 | self._prepare_pkg_transaction() | ||
| 752 | 555 | ||
| 753 | ''' | 556 | bad_recommendations = self.d.getVar('BAD_RECOMMENDATIONS') |
| 754 | Create configs for rpm and smart, and multilib is supported | 557 | package_exclude = self.d.getVar('PACKAGE_EXCLUDE') |
| 755 | ''' | 558 | exclude_pkgs = (bad_recommendations.split() if bad_recommendations else []) + (package_exlcude.split() if package_exclude else []) |
| 756 | def create_configs(self): | ||
| 757 | target_arch = self.d.getVar('TARGET_ARCH') | ||
| 758 | platform = '%s%s-%s' % (target_arch.replace('-', '_'), | ||
| 759 | self.target_vendor, | ||
| 760 | self.ml_os_list['default']) | ||
| 761 | |||
| 762 | # List must be prefered to least preferred order | ||
| 763 | default_platform_extra = list() | ||
| 764 | platform_extra = list() | ||
| 765 | bbextendvariant = self.d.getVar('BBEXTENDVARIANT') or "" | ||
| 766 | for mlib in self.ml_os_list: | ||
| 767 | for arch in self.ml_prefix_list[mlib]: | ||
| 768 | plt = arch.replace('-', '_') + '-.*-' + self.ml_os_list[mlib] | ||
| 769 | if mlib == bbextendvariant: | ||
| 770 | if plt not in default_platform_extra: | ||
| 771 | default_platform_extra.append(plt) | ||
| 772 | else: | ||
| 773 | if plt not in platform_extra: | ||
| 774 | platform_extra.append(plt) | ||
| 775 | platform_extra = default_platform_extra + platform_extra | ||
| 776 | 559 | ||
| 777 | self._create_configs(platform, platform_extra) | 560 | output = self._invoke_dnf((["--skip-broken"] if attempt_only else []) + |
| 561 | (["-x", ",".join(exclude_pkgs)] if len(exclude_pkgs) > 0 else []) + | ||
| 562 | (["--setopt=install_weak_deps=False"] if self.d.getVar('NO_RECOMMENDATIONS') == 1 else []) + | ||
| 563 | ["--nogpgcheck", "install"] + | ||
| 564 | pkgs) | ||
| 778 | 565 | ||
| 779 | #takes array args | 566 | failed_scriptlets_pkgnames = collections.OrderedDict() |
| 780 | def _invoke_smart(self, args): | 567 | for line in output.splitlines(): |
| 781 | cmd = [self.smart_cmd] + self.smart_opt + args | 568 | if line.startswith("Non-fatal POSTIN scriptlet failure in rpm package"): |
| 782 | # bb.note(cmd) | 569 | failed_scriptlets_pkgnames[line.split()[-1]] = True |
| 783 | try: | ||
| 784 | complementary_pkgs = subprocess.check_output(cmd,stderr=subprocess.STDOUT).decode("utf-8") | ||
| 785 | # bb.note(complementary_pkgs) | ||
| 786 | return complementary_pkgs | ||
| 787 | except subprocess.CalledProcessError as e: | ||
| 788 | bb.fatal("Could not invoke smart. Command " | ||
| 789 | "'%s' returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8"))) | ||
| 790 | 570 | ||
| 791 | def _search_pkg_name_in_feeds(self, pkg, feed_archs): | 571 | for pkg in failed_scriptlets_pkgnames.keys(): |
| 792 | for arch in feed_archs: | 572 | self.save_rpmpostinst(pkg) |
| 793 | arch = arch.replace('-', '_') | ||
| 794 | regex_match = re.compile(r"^%s-[^-]*-[^-]*@%s$" % \ | ||
| 795 | (re.escape(pkg), re.escape(arch))) | ||
| 796 | for p in self.fullpkglist: | ||
| 797 | if regex_match.match(p) is not None: | ||
| 798 | # First found is best match | ||
| 799 | # bb.note('%s -> %s' % (pkg, pkg + '@' + arch)) | ||
| 800 | return pkg + '@' + arch | ||
| 801 | |||
| 802 | # Search provides if not found by pkgname. | ||
| 803 | bb.note('Not found %s by name, searching provides ...' % pkg) | ||
| 804 | cmd = [self.smart_cmd] + self.smart_opt + ["query", "--provides", pkg, | ||
| 805 | "--show-format=$name-$version"] | ||
| 806 | bb.note('cmd: %s' % ' '.join(cmd)) | ||
| 807 | ps = subprocess.Popen(cmd, stdout=subprocess.PIPE) | ||
| 808 | try: | ||
| 809 | output = subprocess.check_output(["sed", "-ne", "s/ *Provides://p"], | ||
| 810 | stdin=ps.stdout, stderr=subprocess.STDOUT).decode("utf-8") | ||
| 811 | # Found a provider | ||
| 812 | if output: | ||
| 813 | bb.note('Found providers for %s: %s' % (pkg, output)) | ||
| 814 | for p in output.split(): | ||
| 815 | for arch in feed_archs: | ||
| 816 | arch = arch.replace('-', '_') | ||
| 817 | if p.rstrip().endswith('@' + arch): | ||
| 818 | return p | ||
| 819 | except subprocess.CalledProcessError as e: | ||
| 820 | bb.error("Failed running smart query on package %s." % pkg) | ||
| 821 | 573 | ||
| 822 | return "" | 574 | def remove(self, pkgs, with_dependencies = True): |
| 575 | if len(pkgs) == 0: | ||
| 576 | return | ||
| 577 | self._prepare_pkg_transaction() | ||
| 823 | 578 | ||
| 824 | ''' | 579 | if with_dependencies: |
| 825 | Translate the OE multilib format names to the RPM/Smart format names | 580 | self._invoke_dnf(["remove"] + pkgs) |
| 826 | It searched the RPM/Smart format names in probable multilib feeds first, | 581 | else: |
| 827 | and then searched the default base feed. | 582 | cmd = bb.utils.which(os.getenv('PATH'), "rpm") |
| 828 | ''' | 583 | args = ["-e", "--nodeps", "--root=%s" %self.target_rootfs] |
| 829 | def _pkg_translate_oe_to_smart(self, pkgs, attempt_only=False): | ||
| 830 | new_pkgs = list() | ||
| 831 | |||
| 832 | for pkg in pkgs: | ||
| 833 | new_pkg = pkg | ||
| 834 | # Search new_pkg in probable multilibs first | ||
| 835 | for mlib in self.ml_prefix_list: | ||
| 836 | # Jump the default archs | ||
| 837 | if mlib == 'default': | ||
| 838 | continue | ||
| 839 | 584 | ||
| 840 | subst = pkg.replace(mlib + '-', '') | ||
| 841 | # if the pkg in this multilib feed | ||
| 842 | if subst != pkg: | ||
| 843 | feed_archs = self.ml_prefix_list[mlib] | ||
| 844 | new_pkg = self._search_pkg_name_in_feeds(subst, feed_archs) | ||
| 845 | if not new_pkg: | ||
| 846 | # Failed to translate, package not found! | ||
| 847 | err_msg = '%s not found in the %s feeds (%s) in %s.' % \ | ||
| 848 | (pkg, mlib, " ".join(feed_archs), self.d.getVar('DEPLOY_DIR_RPM')) | ||
| 849 | if not attempt_only: | ||
| 850 | bb.error(err_msg) | ||
| 851 | bb.fatal("This is often caused by an empty package declared " \ | ||
| 852 | "in a recipe's PACKAGES variable. (Empty packages are " \ | ||
| 853 | "not constructed unless ALLOW_EMPTY_<pkg> = '1' is used.)") | ||
| 854 | bb.warn(err_msg) | ||
| 855 | else: | ||
| 856 | new_pkgs.append(new_pkg) | ||
| 857 | |||
| 858 | break | ||
| 859 | |||
| 860 | # Apparently not a multilib package... | ||
| 861 | if pkg == new_pkg: | ||
| 862 | # Search new_pkg in default archs | ||
| 863 | default_archs = self.ml_prefix_list['default'] | ||
| 864 | new_pkg = self._search_pkg_name_in_feeds(pkg, default_archs) | ||
| 865 | if not new_pkg: | ||
| 866 | err_msg = '%s not found in the feeds (%s) in %s.' % \ | ||
| 867 | (pkg, " ".join(default_archs), self.d.getVar('DEPLOY_DIR_RPM')) | ||
| 868 | if not attempt_only: | ||
| 869 | bb.error(err_msg) | ||
| 870 | bb.fatal("This is often caused by an empty package declared " \ | ||
| 871 | "in a recipe's PACKAGES variable. (Empty packages are " \ | ||
| 872 | "not constructed unless ALLOW_EMPTY_<pkg> = '1' is used.)") | ||
| 873 | bb.warn(err_msg) | ||
| 874 | else: | ||
| 875 | new_pkgs.append(new_pkg) | ||
| 876 | |||
| 877 | return new_pkgs | ||
| 878 | |||
| 879 | def _create_configs(self, platform, platform_extra): | ||
| 880 | # Setup base system configuration | ||
| 881 | bb.note("configuring RPM platform settings") | ||
| 882 | |||
| 883 | # Configure internal RPM environment when using Smart | ||
| 884 | os.environ['RPM_ETCRPM'] = self.etcrpm_dir | ||
| 885 | bb.utils.mkdirhier(self.etcrpm_dir) | ||
| 886 | |||
| 887 | # Setup temporary directory -- install... | ||
| 888 | if os.path.exists(self.install_dir_path): | ||
| 889 | bb.utils.remove(self.install_dir_path, True) | ||
| 890 | bb.utils.mkdirhier(os.path.join(self.install_dir_path, 'tmp')) | ||
| 891 | |||
| 892 | channel_priority = 5 | ||
| 893 | platform_dir = os.path.join(self.etcrpm_dir, "platform") | ||
| 894 | sdkos = self.d.getVar("SDK_OS") | ||
| 895 | with open(platform_dir, "w+") as platform_fd: | ||
| 896 | platform_fd.write(platform + '\n') | ||
| 897 | for pt in platform_extra: | ||
| 898 | channel_priority += 5 | ||
| 899 | if sdkos: | ||
| 900 | tmp = re.sub("-%s$" % sdkos, "-%s\n" % sdkos, pt) | ||
| 901 | tmp = re.sub("-linux.*$", "-linux.*\n", tmp) | ||
| 902 | platform_fd.write(tmp) | ||
| 903 | |||
| 904 | # Tell RPM that the "/" directory exist and is available | ||
| 905 | bb.note("configuring RPM system provides") | ||
| 906 | sysinfo_dir = os.path.join(self.etcrpm_dir, "sysinfo") | ||
| 907 | bb.utils.mkdirhier(sysinfo_dir) | ||
| 908 | with open(os.path.join(sysinfo_dir, "Dirnames"), "w+") as dirnames: | ||
| 909 | dirnames.write("/\n") | ||
| 910 | |||
| 911 | if self.providename: | ||
| 912 | providename_dir = os.path.join(sysinfo_dir, "Providename") | ||
| 913 | if not os.path.exists(providename_dir): | ||
| 914 | providename_content = '\n'.join(self.providename) | ||
| 915 | providename_content += '\n' | ||
| 916 | open(providename_dir, "w+").write(providename_content) | ||
| 917 | |||
| 918 | # Configure RPM... we enforce these settings! | ||
| 919 | bb.note("configuring RPM DB settings") | ||
| 920 | # After change the __db.* cache size, log file will not be | ||
| 921 | # generated automatically, that will raise some warnings, | ||
| 922 | # so touch a bare log for rpm write into it. | ||
| 923 | rpmlib_log = os.path.join(self.image_rpmlib, 'log', 'log.0000000001') | ||
| 924 | if not os.path.exists(rpmlib_log): | ||
| 925 | bb.utils.mkdirhier(os.path.join(self.image_rpmlib, 'log')) | ||
| 926 | open(rpmlib_log, 'w+').close() | ||
| 927 | |||
| 928 | DB_CONFIG_CONTENT = "# ================ Environment\n" \ | ||
| 929 | "set_data_dir .\n" \ | ||
| 930 | "set_create_dir .\n" \ | ||
| 931 | "set_lg_dir ./log\n" \ | ||
| 932 | "set_tmp_dir ./tmp\n" \ | ||
| 933 | "set_flags db_log_autoremove on\n" \ | ||
| 934 | "\n" \ | ||
| 935 | "# -- thread_count must be >= 8\n" \ | ||
| 936 | "set_thread_count 64\n" \ | ||
| 937 | "\n" \ | ||
| 938 | "# ================ Logging\n" \ | ||
| 939 | "\n" \ | ||
| 940 | "# ================ Memory Pool\n" \ | ||
| 941 | "set_cachesize 0 1048576 0\n" \ | ||
| 942 | "set_mp_mmapsize 268435456\n" \ | ||
| 943 | "\n" \ | ||
| 944 | "# ================ Locking\n" \ | ||
| 945 | "set_lk_max_locks 16384\n" \ | ||
| 946 | "set_lk_max_lockers 16384\n" \ | ||
| 947 | "set_lk_max_objects 16384\n" \ | ||
| 948 | "mutex_set_max 163840\n" \ | ||
| 949 | "\n" \ | ||
| 950 | "# ================ Replication\n" | ||
| 951 | |||
| 952 | db_config_dir = os.path.join(self.image_rpmlib, 'DB_CONFIG') | ||
| 953 | if not os.path.exists(db_config_dir): | ||
| 954 | open(db_config_dir, 'w+').write(DB_CONFIG_CONTENT) | ||
| 955 | |||
| 956 | # Create database so that smart doesn't complain (lazy init) | ||
| 957 | cmd = [self.rpm_cmd, '--root', self.target_rootfs, '--dbpath', '/var/lib/rpm', '-qa'] | ||
| 958 | try: | ||
| 959 | subprocess.check_output(cmd, stderr=subprocess.STDOUT) | ||
| 960 | except subprocess.CalledProcessError as e: | ||
| 961 | bb.fatal("Create rpm database failed. Command '%s' " | ||
| 962 | "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8"))) | ||
| 963 | # Import GPG key to RPM database of the target system | ||
| 964 | if self.d.getVar('RPM_SIGN_PACKAGES') == '1': | ||
| 965 | pubkey_path = self.d.getVar('RPM_GPG_PUBKEY') | ||
| 966 | cmd = [self.rpm_cmd, '--root', self.target_rootfs, '--dbpath', '/var/lib/rpm', '--import', pubkey_path] | ||
| 967 | try: | 585 | try: |
| 968 | subprocess.check_output(cmd, stderr=subprocess.STDOUT) | 586 | output = subprocess.check_output([cmd] + args + pkgs, stderr=subprocess.STDOUT).decode("utf-8") |
| 969 | except subprocess.CalledProcessError as e: | 587 | except subprocess.CalledProcessError as e: |
| 970 | bb.fatal("Import GPG key failed. Command '%s' " | 588 | bb.fatal("Could not invoke rpm. Command " |
| 971 | "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8"))) | 589 | "'%s' returned %d:\n%s" % (' '.join([cmd] + args + pkgs), e.returncode, e.output.decode("utf-8"))) |
| 972 | |||
| 973 | |||
| 974 | # Configure smart | ||
| 975 | bb.note("configuring Smart settings") | ||
| 976 | bb.utils.remove(os.path.join(self.target_rootfs, 'var/lib/smart'), | ||
| 977 | True) | ||
| 978 | self._invoke_smart(['config', '--set', 'rpm-root=%s' % self.target_rootfs]) | ||
| 979 | self._invoke_smart(['config', '--set', 'rpm-dbpath=/var/lib/rpm']) | ||
| 980 | self._invoke_smart(['config', '--set', 'rpm-extra-macros._var=%s' % | ||
| 981 | self.d.getVar('localstatedir')]) | ||
| 982 | cmd = ["config", "--set", "rpm-extra-macros._tmppath=/%s/tmp" % self.install_dir_name] | ||
| 983 | |||
| 984 | prefer_color = self.d.getVar('RPM_PREFER_ELF_ARCH') | ||
| 985 | if prefer_color: | ||
| 986 | if prefer_color not in ['0', '1', '2', '4']: | ||
| 987 | bb.fatal("Invalid RPM_PREFER_ELF_ARCH: %s, it should be one of:\n" | ||
| 988 | "\t1: ELF32 wins\n" | ||
| 989 | "\t2: ELF64 wins\n" | ||
| 990 | "\t4: ELF64 N32 wins (mips64 or mips64el only)" % | ||
| 991 | prefer_color) | ||
| 992 | if prefer_color == "4" and self.d.getVar("TUNE_ARCH") not in \ | ||
| 993 | ['mips64', 'mips64el']: | ||
| 994 | bb.fatal("RPM_PREFER_ELF_ARCH = \"4\" is for mips64 or mips64el " | ||
| 995 | "only.") | ||
| 996 | self._invoke_smart(['config', '--set', 'rpm-extra-macros._prefer_color=%s' | ||
| 997 | % prefer_color]) | ||
| 998 | |||
| 999 | self._invoke_smart(cmd) | ||
| 1000 | self._invoke_smart(['config', '--set', 'rpm-ignoresize=1']) | ||
| 1001 | |||
| 1002 | # Write common configuration for host and target usage | ||
| 1003 | self._invoke_smart(['config', '--set', 'rpm-nolinktos=1']) | ||
| 1004 | self._invoke_smart(['config', '--set', 'rpm-noparentdirs=1']) | ||
| 1005 | check_signature = self.d.getVar('RPM_CHECK_SIGNATURES') | ||
| 1006 | if check_signature and check_signature.strip() == "0": | ||
| 1007 | self._invoke_smart(['config', '--set rpm-check-signatures=false']) | ||
| 1008 | for i in self.d.getVar('BAD_RECOMMENDATIONS').split(): | ||
| 1009 | self._invoke_smart(['flag', '--set', 'ignore-recommends', i]) | ||
| 1010 | |||
| 1011 | # Do the following configurations here, to avoid them being | ||
| 1012 | # saved for field upgrade | ||
| 1013 | if self.d.getVar('NO_RECOMMENDATIONS').strip() == "1": | ||
| 1014 | self._invoke_smart(['config', '--set', 'ignore-all-recommends=1']) | ||
| 1015 | pkg_exclude = self.d.getVar('PACKAGE_EXCLUDE') or "" | ||
| 1016 | for i in pkg_exclude.split(): | ||
| 1017 | self._invoke_smart(['flag', '--set', 'exclude-packages', i]) | ||
| 1018 | |||
| 1019 | # Optional debugging | ||
| 1020 | # self._invoke_smart(['config', '--set', 'rpm-log-level=debug']) | ||
| 1021 | # cmd = ['config', '--set', 'rpm-log-file=/tmp/smart-debug-logfile'] | ||
| 1022 | # self._invoke_smart(cmd) | ||
| 1023 | ch_already_added = [] | ||
| 1024 | for canonical_arch in platform_extra: | ||
| 1025 | arch = canonical_arch.split('-')[0] | ||
| 1026 | arch_channel = os.path.join(self.d.getVar('WORKDIR'), 'rpms', arch) | ||
| 1027 | oe.path.remove(arch_channel) | ||
| 1028 | deploy_arch_dir = os.path.join(self.deploy_dir, arch) | ||
| 1029 | if not os.path.exists(deploy_arch_dir): | ||
| 1030 | continue | ||
| 1031 | |||
| 1032 | lockfilename = self.d.getVar('DEPLOY_DIR_RPM') + "/rpm.lock" | ||
| 1033 | lf = bb.utils.lockfile(lockfilename, False) | ||
| 1034 | oe.path.copyhardlinktree(deploy_arch_dir, arch_channel) | ||
| 1035 | bb.utils.unlockfile(lf) | ||
| 1036 | |||
| 1037 | if not arch in ch_already_added: | ||
| 1038 | bb.note('Adding Smart channel %s (%s)' % | ||
| 1039 | (arch, channel_priority)) | ||
| 1040 | self._invoke_smart(['channel', '--add', arch, 'type=rpm-md', | ||
| 1041 | 'baseurl=%s' % arch_channel, '-y']) | ||
| 1042 | self._invoke_smart(['channel', '--set', arch, 'priority=%d' % | ||
| 1043 | channel_priority]) | ||
| 1044 | channel_priority -= 5 | ||
| 1045 | |||
| 1046 | ch_already_added.append(arch) | ||
| 1047 | |||
| 1048 | bb.note('adding Smart RPM DB channel') | ||
| 1049 | self._invoke_smart(['channel', '--add', 'rpmsys', 'type=rpm-sys', '-y']) | ||
| 1050 | |||
| 1051 | # Construct install scriptlet wrapper. | ||
| 1052 | # Scripts need to be ordered when executed, this ensures numeric order. | ||
| 1053 | # If we ever run into needing more the 899 scripts, we'll have to. | ||
| 1054 | # change num to start with 1000. | ||
| 1055 | # | ||
| 1056 | scriptletcmd = "$2 $1/$3 $4\n" | ||
| 1057 | scriptpath = "$1/$3" | ||
| 1058 | |||
| 1059 | # When self.debug_level >= 3, also dump the content of the | ||
| 1060 | # executed scriptlets and how they get invoked. We have to | ||
| 1061 | # replace "exit 1" and "ERR" because printing those as-is | ||
| 1062 | # would trigger a log analysis failure. | ||
| 1063 | if self.debug_level >= 3: | ||
| 1064 | dump_invocation = 'echo "Executing ${name} ${kind} with: ' + scriptletcmd + '"\n' | ||
| 1065 | dump_script = 'cat ' + scriptpath + '| sed -e "s/exit 1/exxxit 1/g" -e "s/ERR/IRR/g"; echo\n' | ||
| 1066 | else: | ||
| 1067 | dump_invocation = 'echo "Executing ${name} ${kind}"\n' | ||
| 1068 | dump_script = '' | ||
| 1069 | |||
| 1070 | SCRIPTLET_FORMAT = "#!/bin/bash\n" \ | ||
| 1071 | "\n" \ | ||
| 1072 | "export PATH=%s\n" \ | ||
| 1073 | "export D=%s\n" \ | ||
| 1074 | 'export OFFLINE_ROOT="$D"\n' \ | ||
| 1075 | 'export IPKG_OFFLINE_ROOT="$D"\n' \ | ||
| 1076 | 'export OPKG_OFFLINE_ROOT="$D"\n' \ | ||
| 1077 | "export INTERCEPT_DIR=%s\n" \ | ||
| 1078 | "export NATIVE_ROOT=%s\n" \ | ||
| 1079 | "\n" \ | ||
| 1080 | "name=`head -1 " + scriptpath + " | cut -d\' \' -f 2`\n" \ | ||
| 1081 | "kind=`head -1 " + scriptpath + " | cut -d\' \' -f 4`\n" \ | ||
| 1082 | + dump_invocation \ | ||
| 1083 | + dump_script \ | ||
| 1084 | + scriptletcmd + \ | ||
| 1085 | "ret=$?\n" \ | ||
| 1086 | "echo Result of ${name} ${kind}: ${ret}\n" \ | ||
| 1087 | "if [ ${ret} -ne 0 ]; then\n" \ | ||
| 1088 | " if [ $4 -eq 1 ]; then\n" \ | ||
| 1089 | " mkdir -p $1/etc/rpm-postinsts\n" \ | ||
| 1090 | " num=100\n" \ | ||
| 1091 | " while [ -e $1/etc/rpm-postinsts/${num}-* ]; do num=$((num + 1)); done\n" \ | ||
| 1092 | ' echo "#!$2" > $1/etc/rpm-postinsts/${num}-${name}\n' \ | ||
| 1093 | ' echo "# Arg: $4" >> $1/etc/rpm-postinsts/${num}-${name}\n' \ | ||
| 1094 | " cat " + scriptpath + " >> $1/etc/rpm-postinsts/${num}-${name}\n" \ | ||
| 1095 | " chmod +x $1/etc/rpm-postinsts/${num}-${name}\n" \ | ||
| 1096 | ' echo "Info: deferring ${name} ${kind} install scriptlet to first boot"\n' \ | ||
| 1097 | " else\n" \ | ||
| 1098 | ' echo "Error: ${name} ${kind} remove scriptlet failed"\n' \ | ||
| 1099 | " fi\n" \ | ||
| 1100 | "fi\n" | ||
| 1101 | |||
| 1102 | intercept_dir = self.d.expand('${WORKDIR}/intercept_scripts') | ||
| 1103 | native_root = self.d.getVar('STAGING_DIR_NATIVE') | ||
| 1104 | scriptlet_content = SCRIPTLET_FORMAT % (os.environ['PATH'], | ||
| 1105 | self.target_rootfs, | ||
| 1106 | intercept_dir, | ||
| 1107 | native_root) | ||
| 1108 | open(self.scriptlet_wrapper, 'w+').write(scriptlet_content) | ||
| 1109 | |||
| 1110 | bb.note("configuring RPM cross-install scriptlet_wrapper") | ||
| 1111 | os.chmod(self.scriptlet_wrapper, 0o755) | ||
| 1112 | cmd = ['config', '--set', 'rpm-extra-macros._cross_scriptlet_wrapper=%s' % | ||
| 1113 | self.scriptlet_wrapper] | ||
| 1114 | self._invoke_smart(cmd) | ||
| 1115 | |||
| 1116 | # Debug to show smart config info | ||
| 1117 | # bb.note(self._invoke_smart(['config', '--show'])) | ||
| 1118 | |||
| 1119 | def update(self): | ||
| 1120 | self._invoke_smart(['update', 'rpmsys']) | ||
| 1121 | |||
| 1122 | def get_rdepends_recursively(self, pkgs): | ||
| 1123 | # pkgs will be changed during the loop, so use [:] to make a copy. | ||
| 1124 | for pkg in pkgs[:]: | ||
| 1125 | sub_data = oe.packagedata.read_subpkgdata(pkg, self.d) | ||
| 1126 | sub_rdep = sub_data.get("RDEPENDS_" + pkg) | ||
| 1127 | if not sub_rdep: | ||
| 1128 | continue | ||
| 1129 | done = list(bb.utils.explode_dep_versions2(sub_rdep).keys()) | ||
| 1130 | next = done | ||
| 1131 | # Find all the rdepends on dependency chain | ||
| 1132 | while next: | ||
| 1133 | new = [] | ||
| 1134 | for sub_pkg in next: | ||
| 1135 | sub_data = oe.packagedata.read_subpkgdata(sub_pkg, self.d) | ||
| 1136 | sub_pkg_rdep = sub_data.get("RDEPENDS_" + sub_pkg) | ||
| 1137 | if not sub_pkg_rdep: | ||
| 1138 | continue | ||
| 1139 | for p in bb.utils.explode_dep_versions2(sub_pkg_rdep): | ||
| 1140 | # Already handled, skip it. | ||
| 1141 | if p in done or p in pkgs: | ||
| 1142 | continue | ||
| 1143 | # It's a new dep | ||
| 1144 | if oe.packagedata.has_subpkgdata(p, self.d): | ||
| 1145 | done.append(p) | ||
| 1146 | new.append(p) | ||
| 1147 | next = new | ||
| 1148 | pkgs.extend(done) | ||
| 1149 | return pkgs | ||
| 1150 | |||
| 1151 | ''' | ||
| 1152 | Install pkgs with smart, the pkg name is oe format | ||
| 1153 | ''' | ||
| 1154 | def install(self, pkgs, attempt_only=False): | ||
| 1155 | |||
| 1156 | if not pkgs: | ||
| 1157 | bb.note("There are no packages to install") | ||
| 1158 | return | ||
| 1159 | bb.note("Installing the following packages: %s" % ' '.join(pkgs)) | ||
| 1160 | if not attempt_only: | ||
| 1161 | # Pull in multilib requires since rpm may not pull in them | ||
| 1162 | # correctly, for example, | ||
| 1163 | # lib32-packagegroup-core-standalone-sdk-target requires | ||
| 1164 | # lib32-libc6, but rpm may pull in libc6 rather than lib32-libc6 | ||
| 1165 | # since it doesn't know mlprefix (lib32-), bitbake knows it and | ||
| 1166 | # can handle it well, find out the RDEPENDS on the chain will | ||
| 1167 | # fix the problem. Both do_rootfs and do_populate_sdk have this | ||
| 1168 | # issue. | ||
| 1169 | # The attempt_only packages don't need this since they are | ||
| 1170 | # based on the installed ones. | ||
| 1171 | # | ||
| 1172 | # Separate pkgs into two lists, one is multilib, the other one | ||
| 1173 | # is non-multilib. | ||
| 1174 | ml_pkgs = [] | ||
| 1175 | non_ml_pkgs = pkgs[:] | ||
| 1176 | for pkg in pkgs: | ||
| 1177 | for mlib in (self.d.getVar("MULTILIB_VARIANTS") or "").split(): | ||
| 1178 | if pkg.startswith(mlib + '-'): | ||
| 1179 | ml_pkgs.append(pkg) | ||
| 1180 | non_ml_pkgs.remove(pkg) | ||
| 1181 | |||
| 1182 | if len(ml_pkgs) > 0 and len(non_ml_pkgs) > 0: | ||
| 1183 | # Found both foo and lib-foo | ||
| 1184 | ml_pkgs = self.get_rdepends_recursively(ml_pkgs) | ||
| 1185 | non_ml_pkgs = self.get_rdepends_recursively(non_ml_pkgs) | ||
| 1186 | # Longer list makes smart slower, so only keep the pkgs | ||
| 1187 | # which have the same BPN, and smart can handle others | ||
| 1188 | # correctly. | ||
| 1189 | pkgs_new = [] | ||
| 1190 | for pkg in non_ml_pkgs: | ||
| 1191 | for mlib in (self.d.getVar("MULTILIB_VARIANTS") or "").split(): | ||
| 1192 | mlib_pkg = mlib + "-" + pkg | ||
| 1193 | if mlib_pkg in ml_pkgs: | ||
| 1194 | pkgs_new.append(pkg) | ||
| 1195 | pkgs_new.append(mlib_pkg) | ||
| 1196 | for pkg in pkgs: | ||
| 1197 | if pkg not in pkgs_new: | ||
| 1198 | pkgs_new.append(pkg) | ||
| 1199 | pkgs = pkgs_new | ||
| 1200 | new_depends = {} | ||
| 1201 | deps = bb.utils.explode_dep_versions2(" ".join(pkgs)) | ||
| 1202 | for depend in deps: | ||
| 1203 | data = oe.packagedata.read_subpkgdata(depend, self.d) | ||
| 1204 | key = "PKG_%s" % depend | ||
| 1205 | if key in data: | ||
| 1206 | new_depend = data[key] | ||
| 1207 | else: | ||
| 1208 | new_depend = depend | ||
| 1209 | new_depends[new_depend] = deps[depend] | ||
| 1210 | pkgs = bb.utils.join_deps(new_depends, commasep=True).split(', ') | ||
| 1211 | pkgs = self._pkg_translate_oe_to_smart(pkgs, attempt_only) | ||
| 1212 | if not pkgs: | ||
| 1213 | bb.note("There are no packages to install") | ||
| 1214 | return | ||
| 1215 | if not attempt_only: | ||
| 1216 | bb.note('to be installed: %s' % ' '.join(pkgs)) | ||
| 1217 | cmd = [self.smart_cmd] + self.smart_opt + ["install", "-y"] + pkgs | ||
| 1218 | bb.note(' '.join(cmd)) | ||
| 1219 | else: | ||
| 1220 | bb.note('installing attempt only packages...') | ||
| 1221 | bb.note('Attempting %s' % ' '.join(pkgs)) | ||
| 1222 | cmd = [self.smart_cmd] + self.smart_opt + ["install", "--attempt", | ||
| 1223 | "-y"] + pkgs | ||
| 1224 | try: | ||
| 1225 | output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8") | ||
| 1226 | bb.note(output) | ||
| 1227 | except subprocess.CalledProcessError as e: | ||
| 1228 | bb.fatal("Unable to install packages. Command '%s' " | ||
| 1229 | "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8"))) | ||
| 1230 | |||
| 1231 | ''' | ||
| 1232 | Remove pkgs with smart, the pkg name is smart/rpm format | ||
| 1233 | ''' | ||
| 1234 | def remove(self, pkgs, with_dependencies=True): | ||
| 1235 | bb.note('to be removed: ' + ' '.join(pkgs)) | ||
| 1236 | |||
| 1237 | if not with_dependencies: | ||
| 1238 | cmd = [self.rpm_cmd] + ["-e", "--nodeps", "--root=%s" % | ||
| 1239 | self.target_rootfs, "--dbpath=/var/lib/rpm", | ||
| 1240 | "--define='_cross_scriptlet_wrapper %s'" % | ||
| 1241 | self.scriptlet_wrapper, | ||
| 1242 | "--define='_tmppath /%s/tmp'" % self.install_dir_name] + pkgs | ||
| 1243 | else: | ||
| 1244 | # for pkg in pkgs: | ||
| 1245 | # bb.note('Debug: What required: %s' % pkg) | ||
| 1246 | # bb.note(self._invoke_smart(['query', pkg, '--show-requiredby'])) | ||
| 1247 | cmd = [self.smart_cmd] + self.smart_opt + ["remove", "-y"] + pkgs | ||
| 1248 | try: | ||
| 1249 | bb.note(' '.join(cmd)) | ||
| 1250 | output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8") | ||
| 1251 | bb.note(output) | ||
| 1252 | except subprocess.CalledProcessError as e: | ||
| 1253 | bb.note("Unable to remove packages. Command '%s' " | ||
| 1254 | "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) | ||
| 1255 | 590 | ||
| 1256 | def upgrade(self): | 591 | def upgrade(self): |
| 1257 | bb.note('smart upgrade') | 592 | self._prepare_pkg_transaction() |
| 1258 | self._invoke_smart(['upgrade']) | 593 | self._invoke_dnf(["upgrade"]) |
| 1259 | |||
| 1260 | def write_index(self): | ||
| 1261 | result = self.indexer.write_index() | ||
| 1262 | 594 | ||
| 1263 | if result is not None: | 595 | def autoremove(self): |
| 1264 | bb.fatal(result) | 596 | self._prepare_pkg_transaction() |
| 597 | self._invoke_dnf(["autoremove"]) | ||
| 1265 | 598 | ||
| 1266 | def remove_packaging_data(self): | 599 | def remove_packaging_data(self): |
| 1267 | bb.utils.remove(self.image_rpmlib, True) | 600 | self._invoke_dnf(["clean", "all"]) |
| 1268 | bb.utils.remove(os.path.join(self.target_rootfs, 'var/lib/smart'), | 601 | for dir in self.packaging_data_dirs: |
| 1269 | True) | 602 | bb.utils.remove(oe.path.join(self.target_rootfs, dir), True) |
| 1270 | bb.utils.remove(os.path.join(self.target_rootfs, 'var/lib/opkg'), True) | ||
| 1271 | |||
| 1272 | # remove temp directory | ||
| 1273 | bb.utils.remove(self.install_dir_path, True) | ||
| 1274 | 603 | ||
| 1275 | def backup_packaging_data(self): | 604 | def backup_packaging_data(self): |
| 1276 | # Save the rpmlib for increment rpm image generation | 605 | # Save the packaging dirs for increment rpm image generation |
| 1277 | if os.path.exists(self.saved_rpmlib): | 606 | if os.path.exists(self.saved_packaging_data): |
| 1278 | bb.utils.remove(self.saved_rpmlib, True) | 607 | bb.utils.remove(self.saved_packaging_data, True) |
| 1279 | shutil.copytree(self.image_rpmlib, | 608 | for i in self.packaging_data_dirs: |
| 1280 | self.saved_rpmlib, | 609 | source_dir = oe.path.join(self.target_rootfs, i) |
| 1281 | symlinks=True) | 610 | target_dir = oe.path.join(self.saved_packaging_data, i) |
| 611 | shutil.copytree(source_dir, target_dir, symlinks=True) | ||
| 1282 | 612 | ||
| 1283 | def recovery_packaging_data(self): | 613 | def recovery_packaging_data(self): |
| 1284 | # Move the rpmlib back | 614 | # Move the rpmlib back |
| 1285 | if os.path.exists(self.saved_rpmlib): | 615 | if os.path.exists(self.saved_packaging_data): |
| 1286 | if os.path.exists(self.image_rpmlib): | 616 | for i in self.packaging_data_dirs: |
| 1287 | bb.utils.remove(self.image_rpmlib, True) | 617 | target_dir = oe.path.join(self.target_rootfs, i) |
| 1288 | 618 | if os.path.exists(target_dir): | |
| 1289 | bb.note('Recovery packaging data') | 619 | bb.utils.remove(target_dir, True) |
| 1290 | shutil.copytree(self.saved_rpmlib, | 620 | source_dir = oe.path.join(self.saved_packaging_data, i) |
| 1291 | self.image_rpmlib, | 621 | shutil.copytree(source_dir, |
| 622 | target_dir, | ||
| 1292 | symlinks=True) | 623 | symlinks=True) |
| 1293 | 624 | ||
| 1294 | def list_installed(self): | 625 | def list_installed(self): |
| 1295 | return self.pkgs_list.list_pkgs() | 626 | output = self._invoke_dnf(["repoquery", "--installed", "--queryformat", "Package: %{name} %{arch} %{version} %{sourcerpm}\nDependencies:\n%{requires}\nRecommendations:\n%{recommends}\nDependenciesEndHere:\n"], |
| 1296 | 627 | print_output = False) | |
| 1297 | ''' | 628 | packages = {} |
| 1298 | If incremental install, we need to determine what we've got, | 629 | current_package = None |
| 1299 | what we need to add, and what to remove... | 630 | current_deps = None |
| 1300 | The dump_install_solution will dump and save the new install | 631 | current_state = "initial" |
| 1301 | solution. | 632 | for line in output.splitlines(): |
| 1302 | ''' | 633 | if line.startswith("Package:"): |
| 1303 | def dump_install_solution(self, pkgs): | 634 | package_info = line.split(" ")[1:] |
| 1304 | bb.note('creating new install solution for incremental install') | 635 | current_package = package_info[0] |
| 1305 | if len(pkgs) == 0: | 636 | package_arch = package_info[1] |
| 1306 | return | 637 | package_version = package_info[2] |
| 1307 | 638 | package_srpm = package_info[3] | |
| 1308 | pkgs = self._pkg_translate_oe_to_smart(pkgs, False) | 639 | packages[current_package] = {"arch":package_arch, "ver":package_version, "filename":package_srpm} |
| 1309 | install_pkgs = list() | 640 | current_deps = [] |
| 641 | elif line.startswith("Dependencies:"): | ||
| 642 | current_state = "dependencies" | ||
| 643 | elif line.startswith("Recommendations"): | ||
| 644 | current_state = "recommendations" | ||
| 645 | elif line.startswith("DependenciesEndHere:"): | ||
| 646 | current_state = "initial" | ||
| 647 | packages[current_package]["deps"] = current_deps | ||
| 648 | elif len(line) > 0: | ||
| 649 | if current_state == "dependencies": | ||
| 650 | current_deps.append(line) | ||
| 651 | elif current_state == "recommendations": | ||
| 652 | current_deps.append("%s [REC]" % line) | ||
| 653 | |||
| 654 | return packages | ||
| 1310 | 655 | ||
| 1311 | cmd = [self.smart_cmd] + self.smart_opt + ['install', '-y', '--dump'] + pkgs | 656 | def update(self): |
| 657 | self._invoke_dnf(["makecache"]) | ||
| 658 | |||
| 659 | def _invoke_dnf(self, dnf_args, fatal = True, print_output = True ): | ||
| 660 | os.environ['RPM_ETCCONFIGDIR'] = self.target_rootfs | ||
| 661 | |||
| 662 | dnf_cmd = bb.utils.which(os.getenv('PATH'), "dnf-2") | ||
| 663 | standard_dnf_args = (["-v", "--rpmverbosity=debug"] if self.d.getVar('ROOTFS_RPM_DEBUG') else []) + ["-y", | ||
| 664 | "-c", oe.path.join(self.target_rootfs, "etc/dnf/dnf.conf"), | ||
| 665 | "--setopt=reposdir=%s" %(oe.path.join(self.target_rootfs, "etc/yum.repos.d")), | ||
| 666 | "--repofrompath=oe-repo,%s" % (self.rpm_repo_dir), | ||
| 667 | "--installroot=%s" % (self.target_rootfs), | ||
| 668 | "--setopt=logdir=%s" % (self.d.getVar('T')) | ||
| 669 | ] | ||
| 670 | cmd = [dnf_cmd] + standard_dnf_args + dnf_args | ||
| 1312 | try: | 671 | try: |
| 1313 | # Disable rpmsys channel for the fake install | 672 | output = subprocess.check_output(cmd,stderr=subprocess.STDOUT).decode("utf-8") |
| 1314 | self._invoke_smart(['channel', '--disable', 'rpmsys']) | 673 | if print_output: |
| 1315 | 674 | bb.note(output) | |
| 1316 | output = subprocess.check_output(cmd,stderr=subprocess.STDOUT).decode('utf-8') | 675 | return output |
| 1317 | f = open(self.solution_manifest, 'w') | ||
| 1318 | f.write(output) | ||
| 1319 | f.close() | ||
| 1320 | with open(self.solution_manifest, 'r') as manifest: | ||
| 1321 | for pkg in manifest.read().split('\n'): | ||
| 1322 | if '@' in pkg: | ||
| 1323 | install_pkgs.append(pkg.strip()) | ||
| 1324 | except subprocess.CalledProcessError as e: | 676 | except subprocess.CalledProcessError as e: |
| 1325 | bb.note("Unable to dump install packages. Command '%s' " | 677 | if print_output: |
| 1326 | "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) | 678 | (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command " |
| 1327 | # Recovery rpmsys channel | 679 | "'%s' returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8"))) |
| 1328 | self._invoke_smart(['channel', '--enable', 'rpmsys']) | 680 | else: |
| 1329 | return install_pkgs | 681 | (bb.note, bb.fatal)[fatal]("Could not invoke dnf. Command " |
| 682 | "'%s' returned %d:" % (' '.join(cmd), e.returncode)) | ||
| 683 | return e.output.decode("utf-8") | ||
| 684 | |||
| 685 | def dump_install_solution(self, pkgs): | ||
| 686 | open(self.solution_manifest, 'w').write(" ".join(pkgs)) | ||
| 687 | return pkgs | ||
| 1330 | 688 | ||
| 1331 | ''' | ||
| 1332 | If incremental install, we need to determine what we've got, | ||
| 1333 | what we need to add, and what to remove... | ||
| 1334 | The load_old_install_solution will load the previous install | ||
| 1335 | solution | ||
| 1336 | ''' | ||
| 1337 | def load_old_install_solution(self): | 689 | def load_old_install_solution(self): |
| 1338 | bb.note('load old install solution for incremental install') | ||
| 1339 | installed_pkgs = list() | ||
| 1340 | if not os.path.exists(self.solution_manifest): | 690 | if not os.path.exists(self.solution_manifest): |
| 1341 | bb.note('old install solution not exist') | 691 | return [] |
| 1342 | return installed_pkgs | ||
| 1343 | |||
| 1344 | with open(self.solution_manifest, 'r') as manifest: | ||
| 1345 | for pkg in manifest.read().split('\n'): | ||
| 1346 | if '@' in pkg: | ||
| 1347 | installed_pkgs.append(pkg.strip()) | ||
| 1348 | |||
| 1349 | return installed_pkgs | ||
| 1350 | |||
| 1351 | ''' | ||
| 1352 | Dump all available packages in feeds, it should be invoked after the | ||
| 1353 | newest rpm index was created | ||
| 1354 | ''' | ||
| 1355 | def dump_all_available_pkgs(self): | ||
| 1356 | available_manifest = self.d.expand('${T}/saved/available_pkgs.txt') | ||
| 1357 | available_pkgs = list() | ||
| 1358 | cmd = [self.smart_cmd] + self.smart_opt + ['query', '--output', available_manifest] | ||
| 1359 | try: | ||
| 1360 | subprocess.check_output(cmd, stderr=subprocess.STDOUT) | ||
| 1361 | with open(available_manifest, 'r') as manifest: | ||
| 1362 | for pkg in manifest.read().split('\n'): | ||
| 1363 | if '@' in pkg: | ||
| 1364 | available_pkgs.append(pkg.strip()) | ||
| 1365 | except subprocess.CalledProcessError as e: | ||
| 1366 | bb.note("Unable to list all available packages. Command '%s' " | ||
| 1367 | "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8"))) | ||
| 1368 | 692 | ||
| 1369 | self.fullpkglist = available_pkgs | 693 | return open(self.solution_manifest, 'r').read().split() |
| 1370 | 694 | ||
| 1371 | return | 695 | def _script_num_prefix(self, path): |
| 696 | files = os.listdir(path) | ||
| 697 | numbers = set() | ||
| 698 | numbers.add(99) | ||
| 699 | for f in files: | ||
| 700 | numbers.add(int(f.split("-")[0])) | ||
| 701 | return max(numbers) + 1 | ||
| 1372 | 702 | ||
| 1373 | def save_rpmpostinst(self, pkg): | 703 | def save_rpmpostinst(self, pkg): |
| 1374 | mlibs = (self.d.getVar('MULTILIB_GLOBAL_VARIANTS', False) or "").split() | 704 | bb.note("Saving postinstall script of %s" % (pkg)) |
| 1375 | 705 | cmd = bb.utils.which(os.getenv('PATH'), "rpm") | |
| 1376 | new_pkg = pkg | 706 | args = ["-q", "--root=%s" % self.target_rootfs, "--queryformat", "%{postin}", pkg] |
| 1377 | # Remove any multilib prefix from the package name | ||
| 1378 | for mlib in mlibs: | ||
| 1379 | if mlib in pkg: | ||
| 1380 | new_pkg = pkg.replace(mlib + '-', '') | ||
| 1381 | break | ||
| 1382 | |||
| 1383 | bb.note(' * postponing %s' % new_pkg) | ||
| 1384 | saved_dir = self.target_rootfs + self.d.expand('${sysconfdir}/rpm-postinsts/') + new_pkg | ||
| 1385 | |||
| 1386 | cmd = self.rpm_cmd + ' -q --scripts --root ' + self.target_rootfs | ||
| 1387 | cmd += ' --dbpath=/var/lib/rpm ' + new_pkg | ||
| 1388 | cmd += ' | sed -n -e "/^postinstall scriptlet (using .*):$/,/^.* scriptlet (using .*):$/ {/.*/p}"' | ||
| 1389 | cmd += ' | sed -e "/postinstall scriptlet (using \(.*\)):$/d"' | ||
| 1390 | cmd += ' -e "/^.* scriptlet (using .*):$/d" > %s' % saved_dir | ||
| 1391 | |||
| 1392 | try: | ||
| 1393 | bb.note(cmd) | ||
| 1394 | output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).strip().decode("utf-8") | ||
| 1395 | bb.note(output) | ||
| 1396 | os.chmod(saved_dir, 0o755) | ||
| 1397 | except subprocess.CalledProcessError as e: | ||
| 1398 | bb.fatal("Invoke save_rpmpostinst failed. Command '%s' " | ||
| 1399 | "returned %d:\n%s" % (cmd, e.returncode, e.output.decode("utf-8"))) | ||
| 1400 | |||
| 1401 | '''Write common configuration for target usage''' | ||
| 1402 | def rpm_setup_smart_target_config(self): | ||
| 1403 | bb.utils.remove(os.path.join(self.target_rootfs, 'var/lib/smart'), | ||
| 1404 | True) | ||
| 1405 | |||
| 1406 | self._invoke_smart(['config', '--set', 'rpm-nolinktos=1']) | ||
| 1407 | self._invoke_smart(['config', '--set', 'rpm-noparentdirs=1']) | ||
| 1408 | for i in self.d.getVar('BAD_RECOMMENDATIONS').split(): | ||
| 1409 | self._invoke_smart(['flag', '--set', 'ignore-recommends', i]) | ||
| 1410 | self._invoke_smart(['channel', '--add', 'rpmsys', 'type=rpm-sys', '-y']) | ||
| 1411 | |||
| 1412 | ''' | ||
| 1413 | The rpm db lock files were produced after invoking rpm to query on | ||
| 1414 | build system, and they caused the rpm on target didn't work, so we | ||
| 1415 | need to unlock the rpm db by removing the lock files. | ||
| 1416 | ''' | ||
| 1417 | def unlock_rpm_db(self): | ||
| 1418 | # Remove rpm db lock files | ||
| 1419 | rpm_db_locks = glob.glob('%s/var/lib/rpm/__db.*' % self.target_rootfs) | ||
| 1420 | for f in rpm_db_locks: | ||
| 1421 | bb.utils.remove(f, True) | ||
| 1422 | 707 | ||
| 1423 | """ | ||
| 1424 | Returns a dictionary with the package info. | ||
| 1425 | """ | ||
| 1426 | def package_info(self, pkg): | ||
| 1427 | cmd = [self.smart_cmd] + self.smart_opt + ['info', '--urls', pkg] | ||
| 1428 | try: | 708 | try: |
| 1429 | output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode("utf-8") | 709 | output = subprocess.check_output([cmd] + args,stderr=subprocess.STDOUT).decode("utf-8") |
| 1430 | except subprocess.CalledProcessError as e: | 710 | except subprocess.CalledProcessError as e: |
| 1431 | bb.fatal("Unable to list available packages. Command '%s' " | 711 | bb.fatal("Could not invoke rpm. Command " |
| 1432 | "returned %d:\n%s" % (' '.join(cmd), e.returncode, e.output.decode("utf-8"))) | 712 | "'%s' returned %d:\n%s" % (' '.join([cmd] + args), e.returncode, e.output.decode("utf-8"))) |
| 1433 | 713 | ||
| 1434 | # Set default values to avoid UnboundLocalError | 714 | # may need to prepend #!/bin/sh to output |
| 1435 | arch = "" | ||
| 1436 | ver = "" | ||
| 1437 | filename = "" | ||
| 1438 | 715 | ||
| 1439 | #Parse output | 716 | target_path = oe.path.join(self.target_rootfs, self.d.expand('${sysconfdir}/rpm-postinsts/')) |
| 1440 | for line in output.splitlines(): | 717 | bb.utils.mkdirhier(target_path) |
| 1441 | line = line.rstrip() | 718 | num = self._script_num_prefix(target_path) |
| 1442 | if line.startswith("Name:"): | 719 | saved_script_name = oe.path.join(target_path, "%d-%s" % (num, pkg)) |
| 1443 | pkg = line.split(": ")[1] | 720 | open(saved_script_name, 'w').write(output) |
| 1444 | elif line.startswith("Version:"): | 721 | os.chmod(saved_script_name, 0o755) |
| 1445 | tmp_str = line.split(": ")[1] | ||
| 1446 | ver, arch = tmp_str.split("@") | ||
| 1447 | break | ||
| 1448 | |||
| 1449 | # Get filename | ||
| 1450 | index = re.search("^URLs", output, re.MULTILINE) | ||
| 1451 | tmp_str = output[index.end():] | ||
| 1452 | for line in tmp_str.splitlines(): | ||
| 1453 | if "/" in line: | ||
| 1454 | line = line.lstrip() | ||
| 1455 | filename = line.split(" ")[0] | ||
| 1456 | break | ||
| 1457 | |||
| 1458 | # To have the same data type than other package_info methods | ||
| 1459 | filepath = os.path.join(self.deploy_dir, arch, filename) | ||
| 1460 | pkg_dict = {} | ||
| 1461 | pkg_dict[pkg] = {"arch":arch, "ver":ver, "filename":filename, | ||
| 1462 | "filepath": filepath} | ||
| 1463 | |||
| 1464 | return pkg_dict | ||
| 1465 | |||
| 1466 | """ | ||
| 1467 | Returns the path to a tmpdir where resides the contents of a package. | ||
| 1468 | |||
| 1469 | Deleting the tmpdir is responsability of the caller. | ||
| 1470 | 722 | ||
| 1471 | """ | ||
| 1472 | def extract(self, pkg): | 723 | def extract(self, pkg): |
| 1473 | pkg_info = self.package_info(pkg) | 724 | output = self._invoke_dnf(["repoquery", "--queryformat", "%{location}", pkg]) |
| 1474 | if not pkg_info: | 725 | pkg_name = output.splitlines()[-1] |
| 1475 | bb.fatal("Unable to get information for package '%s' while " | 726 | if not pkg_name.endswith(".rpm"): |
| 1476 | "trying to extract the package." % pkg) | 727 | bb.fatal("dnf could not find package %s in repository: %s" %(pkg, output)) |
| 1477 | 728 | pkg_path = oe.path.join(self.rpm_repo_dir, pkg_name) | |
| 1478 | pkg_path = pkg_info[pkg]["filepath"] | ||
| 1479 | 729 | ||
| 1480 | cpio_cmd = bb.utils.which(os.getenv("PATH"), "cpio") | 730 | cpio_cmd = bb.utils.which(os.getenv("PATH"), "cpio") |
| 1481 | rpm2cpio_cmd = bb.utils.which(os.getenv("PATH"), "rpm2cpio") | 731 | rpm2cpio_cmd = bb.utils.which(os.getenv("PATH"), "rpm2cpio") |
| @@ -1725,7 +975,7 @@ class OpkgPM(OpkgDpkgPM): | |||
| 1725 | for uri in feed_uris: | 975 | for uri in feed_uris: |
| 1726 | if archs: | 976 | if archs: |
| 1727 | for arch in archs: | 977 | for arch in archs: |
| 1728 | if (feed_archs is None) and (not os.path.exists(os.path.join(self.deploy_dir, arch))): | 978 | if (feed_archs is None) and (not os.path.exists(oe.path.join(self.deploy_dir, arch))): |
| 1729 | continue | 979 | continue |
| 1730 | bb.note('Adding opkg feed url-%s-%d (%s)' % | 980 | bb.note('Adding opkg feed url-%s-%d (%s)' % |
| 1731 | (arch, uri_iterator, uri)) | 981 | (arch, uri_iterator, uri)) |
diff --git a/meta/lib/oe/rootfs.py b/meta/lib/oe/rootfs.py index 74c643b3b9..5e1c09762a 100644 --- a/meta/lib/oe/rootfs.py +++ b/meta/lib/oe/rootfs.py | |||
| @@ -431,6 +431,8 @@ class RpmRootfs(Rootfs): | |||
| 431 | bb.note('incremental removed: %s' % ' '.join(pkg_to_remove)) | 431 | bb.note('incremental removed: %s' % ' '.join(pkg_to_remove)) |
| 432 | self.pm.remove(pkg_to_remove) | 432 | self.pm.remove(pkg_to_remove) |
| 433 | 433 | ||
| 434 | self.pm.autoremove() | ||
| 435 | |||
| 434 | def _create(self): | 436 | def _create(self): |
| 435 | pkgs_to_install = self.manifest.parse_initial_manifest() | 437 | pkgs_to_install = self.manifest.parse_initial_manifest() |
| 436 | rpm_pre_process_cmds = self.d.getVar('RPM_PREPROCESS_COMMANDS') | 438 | rpm_pre_process_cmds = self.d.getVar('RPM_PREPROCESS_COMMANDS') |
| @@ -444,8 +446,6 @@ class RpmRootfs(Rootfs): | |||
| 444 | if self.progress_reporter: | 446 | if self.progress_reporter: |
| 445 | self.progress_reporter.next_stage() | 447 | self.progress_reporter.next_stage() |
| 446 | 448 | ||
| 447 | self.pm.dump_all_available_pkgs() | ||
| 448 | |||
| 449 | if self.inc_rpm_image_gen == "1": | 449 | if self.inc_rpm_image_gen == "1": |
| 450 | self._create_incremental(pkgs_to_install) | 450 | self._create_incremental(pkgs_to_install) |
| 451 | 451 | ||
| @@ -480,15 +480,13 @@ class RpmRootfs(Rootfs): | |||
| 480 | if self.progress_reporter: | 480 | if self.progress_reporter: |
| 481 | self.progress_reporter.next_stage() | 481 | self.progress_reporter.next_stage() |
| 482 | 482 | ||
| 483 | self._setup_dbg_rootfs(['/etc/rpm', '/var/lib/rpm', '/var/lib/smart']) | 483 | self._setup_dbg_rootfs(['/etc', '/var/lib/rpm', '/var/cache/dnf', '/var/lib/dnf']) |
| 484 | 484 | ||
| 485 | execute_pre_post_process(self.d, rpm_post_process_cmds) | 485 | execute_pre_post_process(self.d, rpm_post_process_cmds) |
| 486 | 486 | ||
| 487 | if self.inc_rpm_image_gen == "1": | 487 | if self.inc_rpm_image_gen == "1": |
| 488 | self.pm.backup_packaging_data() | 488 | self.pm.backup_packaging_data() |
| 489 | 489 | ||
| 490 | self.pm.rpm_setup_smart_target_config() | ||
| 491 | |||
| 492 | if self.progress_reporter: | 490 | if self.progress_reporter: |
| 493 | self.progress_reporter.next_stage() | 491 | self.progress_reporter.next_stage() |
| 494 | 492 | ||
| @@ -526,15 +524,7 @@ class RpmRootfs(Rootfs): | |||
| 526 | self.pm.save_rpmpostinst(pkg) | 524 | self.pm.save_rpmpostinst(pkg) |
| 527 | 525 | ||
| 528 | def _cleanup(self): | 526 | def _cleanup(self): |
| 529 | # during the execution of postprocess commands, rpm is called several | 527 | pass |
| 530 | # times to get the files installed, dependencies, etc. This creates the | ||
| 531 | # __db.00* (Berkeley DB files that hold locks, rpm specific environment | ||
| 532 | # settings, etc.), that should not get into the final rootfs | ||
| 533 | self.pm.unlock_rpm_db() | ||
| 534 | if os.path.isdir(self.pm.install_dir_path + "/tmp") and not os.listdir(self.pm.install_dir_path + "/tmp"): | ||
| 535 | bb.utils.remove(self.pm.install_dir_path + "/tmp", True) | ||
| 536 | if os.path.isdir(self.pm.install_dir_path) and not os.listdir(self.pm.install_dir_path): | ||
| 537 | bb.utils.remove(self.pm.install_dir_path, True) | ||
| 538 | 528 | ||
| 539 | class DpkgOpkgRootfs(Rootfs): | 529 | class DpkgOpkgRootfs(Rootfs): |
| 540 | def __init__(self, d, progress_reporter=None, logcatcher=None): | 530 | def __init__(self, d, progress_reporter=None, logcatcher=None): |
diff --git a/meta/lib/oe/sdk.py b/meta/lib/oe/sdk.py index fef02d0777..deb823b6ec 100644 --- a/meta/lib/oe/sdk.py +++ b/meta/lib/oe/sdk.py | |||
| @@ -130,7 +130,6 @@ class RpmSdk(Sdk): | |||
| 130 | 130 | ||
| 131 | pm.create_configs() | 131 | pm.create_configs() |
| 132 | pm.write_index() | 132 | pm.write_index() |
| 133 | pm.dump_all_available_pkgs() | ||
| 134 | pm.update() | 133 | pm.update() |
| 135 | 134 | ||
| 136 | pkgs = [] | 135 | pkgs = [] |
| @@ -188,7 +187,9 @@ class RpmSdk(Sdk): | |||
| 188 | True).strip('/'), | 187 | True).strip('/'), |
| 189 | ) | 188 | ) |
| 190 | self.mkdirhier(native_sysconf_dir) | 189 | self.mkdirhier(native_sysconf_dir) |
| 191 | for f in glob.glob(os.path.join(self.sdk_output, "etc", "*")): | 190 | for f in glob.glob(os.path.join(self.sdk_output, "etc", "rpm*")): |
| 191 | self.movefile(f, native_sysconf_dir) | ||
| 192 | for f in glob.glob(os.path.join(self.sdk_output, "etc", "dnf", "*")): | ||
| 192 | self.movefile(f, native_sysconf_dir) | 193 | self.movefile(f, native_sysconf_dir) |
| 193 | self.remove(os.path.join(self.sdk_output, "etc"), True) | 194 | self.remove(os.path.join(self.sdk_output, "etc"), True) |
| 194 | 195 | ||
| @@ -350,7 +351,7 @@ def sdk_list_installed_packages(d, target, rootfs_dir=None): | |||
| 350 | if img_type == "rpm": | 351 | if img_type == "rpm": |
| 351 | arch_var = ["SDK_PACKAGE_ARCHS", None][target is True] | 352 | arch_var = ["SDK_PACKAGE_ARCHS", None][target is True] |
| 352 | os_var = ["SDK_OS", None][target is True] | 353 | os_var = ["SDK_OS", None][target is True] |
| 353 | return RpmPkgsList(d, rootfs_dir, arch_var, os_var).list_pkgs() | 354 | return RpmPkgsList(d, rootfs_dir).list_pkgs() |
| 354 | elif img_type == "ipk": | 355 | elif img_type == "ipk": |
| 355 | conf_file_var = ["IPKGCONF_SDK", "IPKGCONF_TARGET"][target is True] | 356 | conf_file_var = ["IPKGCONF_SDK", "IPKGCONF_TARGET"][target is True] |
| 356 | return OpkgPkgsList(d, rootfs_dir, d.getVar(conf_file_var)).list_pkgs() | 357 | return OpkgPkgsList(d, rootfs_dir, d.getVar(conf_file_var)).list_pkgs() |
