mirror of
https://git.yoctoproject.org/poky
synced 2026-03-16 20:29:41 +01:00
When looking at bitbake parsing speed issues, I noticed a lot of weird looking variables from the update-alternatives class. It is possible this was written before variable dependencies could handle flags. It can handle flags now so simplfy the code to take advantage of that and avoid the indirection variables. The win here is a significant reduction in the number of variables, which in turn significantly reduces the looping bitbake's taskhash calculation code needs to do. (From OE-Core rev: bd8fc4c59a137a37bd7a54f398949617982d447e) Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
320 lines
14 KiB
Plaintext
320 lines
14 KiB
Plaintext
#
|
|
# Copyright OpenEmbedded Contributors
|
|
#
|
|
# SPDX-License-Identifier: MIT
|
|
#
|
|
|
|
# This class is used to help the alternatives system which is useful when
|
|
# multiple sources provide the same command. You can use update-alternatives
|
|
# command directly in your recipe, but in most cases this class simplifies
|
|
# that job.
|
|
#
|
|
# To use this class a number of variables should be defined:
|
|
#
|
|
# List all of the alternatives needed by a package:
|
|
# ALTERNATIVE:<pkg> = "name1 name2 name3 ..."
|
|
#
|
|
# i.e. ALTERNATIVE:busybox = "sh sed test bracket"
|
|
#
|
|
# The pathname of the link
|
|
# ALTERNATIVE_LINK_NAME[name] = "target"
|
|
#
|
|
# This is the name of the binary once it's been installed onto the runtime.
|
|
# This name is global to all split packages in this recipe, and should match
|
|
# other recipes with the same functionality.
|
|
# i.e. ALTERNATIVE_LINK_NAME[bracket] = "/usr/bin/["
|
|
#
|
|
# NOTE: If ALTERNATIVE_LINK_NAME is not defined, it defaults to ${bindir}/name
|
|
#
|
|
# The default link to create for all targets
|
|
# ALTERNATIVE_TARGET = "target"
|
|
#
|
|
# This is useful in a multicall binary case
|
|
# i.e. ALTERNATIVE_TARGET = "/bin/busybox"
|
|
#
|
|
# A non-default link to create for a target
|
|
# ALTERNATIVE_TARGET[name] = "target"
|
|
#
|
|
# This is the name of the binary as it's been installed by do_install
|
|
# i.e. ALTERNATIVE_TARGET[sh] = "/bin/bash"
|
|
#
|
|
# A package specific link for a target
|
|
# ALTERNATIVE_TARGET_<pkg>[name] = "target"
|
|
#
|
|
# This is useful when a recipe provides multiple alternatives for the
|
|
# same item.
|
|
#
|
|
# NOTE: If ALTERNATIVE_TARGET is not defined, it will inherit the value
|
|
# from ALTERNATIVE_LINK_NAME.
|
|
#
|
|
# NOTE: If the ALTERNATIVE_LINK_NAME and ALTERNATIVE_TARGET are the same,
|
|
# ALTERNATIVE_TARGET will have '.{BPN}' appended to it. If the file
|
|
# referenced has not been renamed, it will also be renamed. (This avoids
|
|
# the need to rename alternative files in the do_install step, but still
|
|
# supports it if necessary for some reason.)
|
|
#
|
|
# The default priority for any alternatives
|
|
# ALTERNATIVE_PRIORITY = "priority"
|
|
#
|
|
# i.e. default is ALTERNATIVE_PRIORITY = "10"
|
|
#
|
|
# The non-default priority for a specific target
|
|
# ALTERNATIVE_PRIORITY[name] = "priority"
|
|
#
|
|
# The package priority for a specific target
|
|
# ALTERNATIVE_PRIORITY_<pkg>[name] = "priority"
|
|
|
|
ALTERNATIVE_PRIORITY = "10"
|
|
|
|
# We need special processing for vardeps because it can not work on
|
|
# modified flag values. So we aggregate the flags into a new variable
|
|
# and include that variable in the set.
|
|
UPDALTVARS = "ALTERNATIVE ALTERNATIVE_LINK_NAME ALTERNATIVE_TARGET ALTERNATIVE_PRIORITY"
|
|
|
|
PACKAGE_WRITE_DEPS += "virtual/update-alternatives-native"
|
|
|
|
def ua_extend_depends(d):
|
|
if not 'virtual/update-alternatives' in d.getVar('PROVIDES'):
|
|
d.appendVar('DEPENDS', ' virtual/${MLPREFIX}update-alternatives')
|
|
|
|
def update_alternatives_enabled(d):
|
|
# Update Alternatives only works on target packages...
|
|
if bb.data.inherits_class('native', d) or \
|
|
bb.data.inherits_class('cross', d) or bb.data.inherits_class('crosssdk', d) or \
|
|
bb.data.inherits_class('cross-canadian', d):
|
|
return False
|
|
|
|
# Disable when targeting mingw32 (no target support)
|
|
if d.getVar("TARGET_OS") == "mingw32":
|
|
return False
|
|
|
|
return True
|
|
|
|
python __anonymous() {
|
|
if not update_alternatives_enabled(d):
|
|
return
|
|
|
|
# extend the depends to include virtual/update-alternatives
|
|
ua_extend_depends(d)
|
|
}
|
|
|
|
def gen_updatealternativesvars(d):
|
|
ret = []
|
|
pkgs = (d.getVar("PACKAGES") or "").split()
|
|
vars = (d.getVar("UPDALTVARS") or "").split()
|
|
|
|
# First compute them for non_pkg versions
|
|
for v in vars:
|
|
for flag in sorted((d.getVarFlags(v) or {}).keys()):
|
|
if flag == "doc" or flag == "vardeps" or flag == "vardepsexp":
|
|
continue
|
|
ret.append(v + "[" + flag + "]")
|
|
|
|
for p in pkgs:
|
|
for v in vars:
|
|
for flag in sorted((d.getVarFlags("%s:%s" % (v,p)) or {}).keys()):
|
|
if flag == "doc" or flag == "vardeps" or flag == "vardepsexp":
|
|
continue
|
|
ret.append('%s:%s' % (v,p) + "[" + flag + "]")
|
|
|
|
return " ".join(ret)
|
|
|
|
# Now the new stuff, we use a custom function to generate the right values
|
|
populate_packages[vardeps] += "${UPDALTVARS} ${@gen_updatealternativesvars(d)}"
|
|
|
|
# We need to do the rename after the image creation step, but before
|
|
# the split and strip steps.. PACKAGE_PREPROCESS_FUNCS is the right
|
|
# place for that.
|
|
PACKAGE_PREPROCESS_FUNCS += "apply_update_alternative_renames"
|
|
python apply_update_alternative_renames () {
|
|
if not update_alternatives_enabled(d):
|
|
return
|
|
|
|
import re
|
|
|
|
def update_files(alt_target, alt_target_rename, pkg, d):
|
|
f = d.getVar('FILES:' + pkg)
|
|
if f:
|
|
f = re.sub(r'(^|\s)%s(\s|$)' % re.escape (alt_target), r'\1%s\2' % alt_target_rename, f)
|
|
d.setVar('FILES:' + pkg, f)
|
|
|
|
# Check for deprecated usage...
|
|
pn = d.getVar('BPN')
|
|
if d.getVar('ALTERNATIVE_LINKS') != None:
|
|
bb.fatal('%s: Use of ALTERNATIVE_LINKS/ALTERNATIVE_PATH/ALTERNATIVE_NAME is no longer supported, please convert to the updated syntax, see update-alternatives.bbclass for more info.' % pn)
|
|
|
|
# Do actual update alternatives processing
|
|
pkgdest = d.getVar('PKGD')
|
|
for pkg in (d.getVar('PACKAGES') or "").split():
|
|
# If the src == dest, we know we need to rename the dest by appending ${BPN}
|
|
link_rename = []
|
|
for alt_name in (d.getVar('ALTERNATIVE:%s' % pkg) or "").split():
|
|
alt_link = d.getVarFlag('ALTERNATIVE_LINK_NAME', alt_name)
|
|
if not alt_link:
|
|
alt_link = "%s/%s" % (d.getVar('bindir'), alt_name)
|
|
d.setVarFlag('ALTERNATIVE_LINK_NAME', alt_name, alt_link)
|
|
if alt_link.startswith(os.path.join(d.getVar('sysconfdir'), 'init.d')):
|
|
# Managing init scripts does not work (bug #10433), foremost
|
|
# because of a race with update-rc.d
|
|
bb.fatal("Using update-alternatives for managing SysV init scripts is not supported")
|
|
|
|
alt_target = d.getVarFlag('ALTERNATIVE_TARGET_%s' % pkg, alt_name) or d.getVarFlag('ALTERNATIVE_TARGET', alt_name)
|
|
alt_target = alt_target or d.getVar('ALTERNATIVE_TARGET_%s' % pkg) or d.getVar('ALTERNATIVE_TARGET') or alt_link
|
|
# Sometimes alt_target is specified as relative to the link name.
|
|
alt_target = os.path.join(os.path.dirname(alt_link), alt_target)
|
|
|
|
# If the link and target are the same name, we need to rename the target.
|
|
if alt_link == alt_target:
|
|
src = '%s/%s' % (pkgdest, alt_target)
|
|
alt_target_rename = '%s.%s' % (alt_target, pn)
|
|
dest = '%s/%s' % (pkgdest, alt_target_rename)
|
|
if os.path.lexists(dest):
|
|
bb.note('%s: Already renamed: %s' % (pn, alt_target_rename))
|
|
elif os.path.lexists(src):
|
|
if os.path.islink(src):
|
|
# Delay rename of links
|
|
link_rename.append((alt_target, alt_target_rename))
|
|
else:
|
|
bb.note('%s: Rename %s -> %s' % (pn, alt_target, alt_target_rename))
|
|
bb.utils.rename(src, dest)
|
|
update_files(alt_target, alt_target_rename, pkg, d)
|
|
else:
|
|
bb.warn("%s: alternative target (%s or %s) does not exist, skipping..." % (pn, alt_target, alt_target_rename))
|
|
continue
|
|
d.setVarFlag('ALTERNATIVE_TARGET_%s' % pkg, alt_name, alt_target_rename)
|
|
|
|
# Process delayed link names
|
|
# Do these after other renames so we can correct broken links
|
|
for (alt_target, alt_target_rename) in link_rename:
|
|
src = '%s/%s' % (pkgdest, alt_target)
|
|
dest = '%s/%s' % (pkgdest, alt_target_rename)
|
|
link_target = oe.path.realpath(src, pkgdest, True)
|
|
|
|
if os.path.lexists(link_target):
|
|
# Ok, the link_target exists, we can rename
|
|
bb.note('%s: Rename (link) %s -> %s' % (pn, alt_target, alt_target_rename))
|
|
bb.utils.rename(src, dest)
|
|
else:
|
|
# Try to resolve the broken link to link.${BPN}
|
|
link_maybe = '%s.%s' % (os.readlink(src), pn)
|
|
if os.path.lexists(os.path.join(os.path.dirname(src), link_maybe)):
|
|
# Ok, the renamed link target exists.. create a new link, and remove the original
|
|
bb.note('%s: Creating new link %s -> %s' % (pn, alt_target_rename, link_maybe))
|
|
os.symlink(link_maybe, dest)
|
|
os.unlink(src)
|
|
else:
|
|
bb.warn('%s: Unable to resolve dangling symlink: %s' % (pn, alt_target))
|
|
continue
|
|
update_files(alt_target, alt_target_rename, pkg, d)
|
|
}
|
|
|
|
def update_alternatives_alt_targets(d, pkg):
|
|
"""
|
|
Returns the update-alternatives metadata for a package.
|
|
|
|
The returned format is a list of tuples where the tuple contains:
|
|
alt_name: The binary name
|
|
alt_link: The path for the binary (Shared by different packages)
|
|
alt_target: The path for the renamed binary (Unique per package)
|
|
alt_priority: The priority of the alt_target
|
|
|
|
All the alt_targets will be installed into the sysroot. The alt_link is
|
|
a symlink pointing to the alt_target with the highest priority.
|
|
"""
|
|
|
|
pn = d.getVar('BPN')
|
|
pkgdest = d.getVar('PKGD')
|
|
updates = list()
|
|
for alt_name in (d.getVar('ALTERNATIVE:%s' % pkg) or "").split():
|
|
alt_link = d.getVarFlag('ALTERNATIVE_LINK_NAME', alt_name)
|
|
alt_target = d.getVarFlag('ALTERNATIVE_TARGET_%s' % pkg, alt_name) or \
|
|
d.getVarFlag('ALTERNATIVE_TARGET', alt_name) or \
|
|
d.getVar('ALTERNATIVE_TARGET_%s' % pkg) or \
|
|
d.getVar('ALTERNATIVE_TARGET') or \
|
|
alt_link
|
|
alt_priority = d.getVarFlag('ALTERNATIVE_PRIORITY_%s' % pkg, alt_name) or \
|
|
d.getVarFlag('ALTERNATIVE_PRIORITY', alt_name) or \
|
|
d.getVar('ALTERNATIVE_PRIORITY_%s' % pkg) or \
|
|
d.getVar('ALTERNATIVE_PRIORITY')
|
|
|
|
# This shouldn't trigger, as it should have been resolved earlier!
|
|
if alt_link == alt_target:
|
|
bb.note('alt_link == alt_target: %s == %s -- correcting, this should not happen!' % (alt_link, alt_target))
|
|
alt_target = '%s.%s' % (alt_target, pn)
|
|
|
|
if not os.path.lexists('%s/%s' % (pkgdest, alt_target)):
|
|
bb.warn('%s: NOT adding alternative provide %s: %s does not exist' % (pn, alt_link, alt_target))
|
|
continue
|
|
|
|
alt_target = os.path.normpath(alt_target)
|
|
updates.append( (alt_name, alt_link, alt_target, alt_priority) )
|
|
|
|
return updates
|
|
|
|
PACKAGESPLITFUNCS =+ "populate_packages_updatealternatives"
|
|
|
|
python populate_packages_updatealternatives () {
|
|
if not update_alternatives_enabled(d):
|
|
return
|
|
|
|
# Do actual update alternatives processing
|
|
for pkg in (d.getVar('PACKAGES') or "").split():
|
|
# Create post install/removal scripts
|
|
alt_setup_links = ""
|
|
alt_remove_links = ""
|
|
updates = update_alternatives_alt_targets(d, pkg)
|
|
for alt_name, alt_link, alt_target, alt_priority in updates:
|
|
alt_setup_links += '\tupdate-alternatives --install %s %s %s %s\n' % (alt_link, alt_name, alt_target, alt_priority)
|
|
alt_remove_links += '\tupdate-alternatives --remove %s %s\n' % (alt_name, alt_target)
|
|
|
|
if alt_setup_links:
|
|
# RDEPENDS setup
|
|
provider = d.getVar('VIRTUAL-RUNTIME_update-alternatives')
|
|
if provider:
|
|
#bb.note('adding runtime requirement for update-alternatives for %s' % pkg)
|
|
d.appendVar('RDEPENDS:%s' % pkg, ' ' + d.getVar('MLPREFIX', False) + provider)
|
|
|
|
bb.note('adding update-alternatives calls to postinst/prerm for %s' % pkg)
|
|
bb.note('%s' % alt_setup_links)
|
|
postinst = d.getVar('pkg_postinst:%s' % pkg)
|
|
if postinst:
|
|
postinst = alt_setup_links + postinst
|
|
else:
|
|
postinst = '#!/bin/sh\n' + alt_setup_links
|
|
d.setVar('pkg_postinst:%s' % pkg, postinst)
|
|
|
|
bb.note('%s' % alt_remove_links)
|
|
prerm = d.getVar('pkg_prerm:%s' % pkg) or '#!/bin/sh\n'
|
|
prerm += alt_remove_links
|
|
d.setVar('pkg_prerm:%s' % pkg, prerm)
|
|
}
|
|
|
|
python package_do_filedeps:append () {
|
|
if update_alternatives_enabled(d):
|
|
apply_update_alternative_provides(d)
|
|
}
|
|
|
|
def apply_update_alternative_provides(d):
|
|
pn = d.getVar('BPN')
|
|
pkgdest = d.getVar('PKGDEST')
|
|
|
|
for pkg in d.getVar('PACKAGES').split():
|
|
for alt_name in (d.getVar('ALTERNATIVE:%s' % pkg) or "").split():
|
|
alt_link = d.getVarFlag('ALTERNATIVE_LINK_NAME', alt_name)
|
|
alt_target = d.getVarFlag('ALTERNATIVE_TARGET_%s' % pkg, alt_name) or d.getVarFlag('ALTERNATIVE_TARGET', alt_name)
|
|
alt_target = alt_target or d.getVar('ALTERNATIVE_TARGET_%s' % pkg) or d.getVar('ALTERNATIVE_TARGET') or alt_link
|
|
|
|
if alt_link == alt_target:
|
|
bb.warn('%s: alt_link == alt_target: %s == %s' % (pn, alt_link, alt_target))
|
|
alt_target = '%s.%s' % (alt_target, pn)
|
|
|
|
if not os.path.lexists('%s/%s/%s' % (pkgdest, pkg, alt_target)):
|
|
continue
|
|
|
|
# Add file provide
|
|
trans_target = oe.package.file_translate(alt_target)
|
|
d.appendVar('FILERPROVIDES:%s:%s' % (trans_target, pkg), " " + alt_link)
|
|
if not trans_target in (d.getVar('FILERPROVIDESFLIST:%s' % pkg) or ""):
|
|
d.appendVar('FILERPROVIDESFLIST:%s' % pkg, " " + trans_target)
|
|
|