Skip to content

Instantly share code, notes, and snippets.

@chewi
Last active January 12, 2020 22:28
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save chewi/1601684ad8f3cf8de0b786c00fa09b3c to your computer and use it in GitHub Desktop.
Save chewi/1601684ad8f3cf8de0b786c00fa09b3c to your computer and use it in GitHub Desktop.
Migrates a Gentoo ARM system from the old style tuple (CHOST) to the new style tuple ending in hf.
#!/bin/bash
# Abort if things go wrong. Very important!
set -ex
# Fetch the old CHOST from make.conf or profile.
OLD_CHOST=$(portageq envvar CHOST)
# The new CHOST needs to end in hf. The vendor part doesn't really
# matter as long as it's not softfloat or softfp but the convention is
# to replace hardfloat with unknown.
CHOST=${OLD_CHOST/-hardfloat-/-unknown-}hf
case ${OLD_CHOST} in
"")
echo "Unable to determine the old CHOST value. Is make.conf sane? Do you have a valid profile selected?"
exit 1 ;;
*-softfloat-*)
echo "This appears to be a softfloat system so you should not be running this script."
exit 1 ;;
*-softfp-*)
echo "This appears to be a softfp system. Your hardware may be hardfloat-capable but you cannot migrate using this script."
exit 1 ;;
*hf)
echo "You already have the new *hf CHOST? Either you've already successful run this or you're doing something crazy."
exit 1 ;;
esac
# This function is used to skip parts of this script if it is being
# retried following a failure. It checks whether the given atom has
# been built against the new CHOST.
atom_new_chost() {
local path=${VDB}/${1}/CHOST
[[ ! -f ${path} ]] && return 1
[[ $(cat "${path}") == ${CHOST} ]]
}
# This script supports prefixed systems.
EPREFIX=$(portageq envvar EPREFIX)
# The VDB path is needed for atom_new_chost above.
VDB=$(portageq vdb_path)
# Fetch the old profile and convert to the new profile. The new
# profile may already be selected if this script is being retried so
# convert both ways just in case. Note that some profiles like
# hardened are not versioned so these will not change. That is okay.
OLD_PROFILE=$(eselect --brief profile show)
OLD_PROFILE=${OLD_PROFILE/17.0/13.0}
NEW_PROFILE=${OLD_PROFILE/13.0/17.0}
# What are the latest binutils and gcc versions we can install?
BINUTILS_PF=$(portageq best_visible "${EPREFIX}"/ sys-devel/binutils)
GCC_PF=$(portageq best_visible "${EPREFIX}"/ sys-devel/gcc)
# We need the new binutils PV and gcc SLOT for some operations below.
BINUTILS_PV=${BINUTILS_PF#sys-devel/binutils-}
BINUTILS_PV=${BINUTILS_PV%-r*}
GCC_SLOT=$(portageq metadata "${EPREFIX}"/ ebuild "${GCC_PF}" SLOT)
# What libc does this profile use, what is the latest we can install?
LIBC=$(portageq expand_virtual "${EPREFIX}"/ virtual/libc)
LIBC_PF=$(portageq best_visible "${EPREFIX}"/ "${LIBC}")
# What are the latest libtool and perl versions we can install?
LIBTOOL_PF=$(portageq best_visible "${EPREFIX}"/ sys-devel/libtool)
PERL_PF=$(portageq best_visible "${EPREFIX}"/ dev-lang/perl)
# Ensure portage, sandbox, and portage-utils are up to date. Older
# sandbox can block newer libtool. portage-utils is needed for qfile.
emerge -v1u sys-apps/portage sys-apps/sandbox app-portage/portage-utils
# Backup the old toolchain, just in case.
atom_new_chost "${BINUTILS_PF}" ||
quickpkg sys-devel/binutils sys-devel/gcc "${LIBC}"
# Select the new profile.
eselect profile set --force ${NEW_PROFILE}
# If we modify make.conf with the new CHOST now then we won't be able
# to reliably determine the old CHOST when retrying this script. Just
# override with this variable and modify make.conf at the end.
export CHOST
# Build binutils with the new CHOST.
atom_new_chost "${BINUTILS_PF}" ||
emerge -v1 "=${BINUTILS_PF}"
# Remove old binutils versions to avoid a mix-up.
! emerge -C "<${BINUTILS_PF}"
# Explicitly select the newly built binutils.
binutils-config "${CHOST}-${BINUTILS_PV}"
# Build gcc with the new CHOST.
atom_new_chost "${GCC_PF}" ||
emerge -v1 "=${GCC_PF}"
# Remove old gcc versions to avoid a mix-up.
! emerge -C "<${GCC_PF}"
# Explicitly select the newly built gcc.
gcc-config -f "${CHOST}-${GCC_SLOT}"
# Delete old CHOST files from env.d.
find "${EPREFIX}"/etc/env.d/ -regex ".*\\b${OLD_CHOST}\\b.*" -exec rm -v {} +
# Delete old CHOST files from ld.so.conf.d, which may not exist.
[[ -d "${EPREFIX}"/etc/ld.so.conf.d ]] &&
find "${EPREFIX}"/etc/ld.so.conf.d/ -regex ".*\\b${OLD_CHOST}\\b.*" -exec rm -v {} +
# Refresh the environment.
env-update
source "${EPREFIX}"/etc/profile
ldconfig
# Build libc with the new CHOST.
atom_new_chost "${LIBC_PF}" ||
emerge -v1 "=${LIBC_PF}"
# Rebuild libtool and binutils as required for a PIE migration.
if ! atom_new_chost "${LIBTOOL_PF}"; then
emerge -v1 "=${BINUTILS_PF}"
emerge -v1 "=${LIBTOOL_PF}"
fi
# fix_libtool_files.sh is sometimes needed after changing CHOST.
"${EPREFIX}"/usr/share/gcc-data/"${CHOST}/${GCC_SLOT}"/fix_libtool_files.sh "${GCC_SLOT}" --oldarch "${OLD_CHOST}"
# Delete any orphaned files under /usr/bin that are prefixed with the
# old CHOST. This is mainly to clean up files created by gcc-config.
qfile -o "${EPREFIX}"/usr/bin/"${OLD_CHOST}"-* | xargs rm -vf
# Rename any remaining files under /usr/bin that are prefixed with the
# old CHOST so that they now have the new CHOST instead.
find "${EPREFIX}"/usr/bin -name "${OLD_CHOST}-*" -execdir bash -c "X='{}'; mv -v \"\${X}\" \"\${X/${OLD_CHOST}/${CHOST}}\"" \;
# The previous operation will have broken symlinks such as xml2-config
# so replace the old CHOST with the new CHOST in such symlinks.
find "${EPREFIX}"/usr/bin -lname "${OLD_CHOST}-*" -execdir bash -c "X='{}'; ln -snfv \"${CHOST}-\${X##*/}\" \"\${X}\"" \;
# Safely delete broken symlinks and empty directories from the old
# toolchain including the main top-level directory, if possible.
! find "${EPREFIX}/usr/${OLD_CHOST}"/ -xtype l -delete
! find "${EPREFIX}/usr/${OLD_CHOST}"/ -type d -empty -delete
# Python is sensitive to a CHOST change but a sed fix is sufficient.
sed -i "s/${OLD_CHOST}/${CHOST}/g" "${EPREFIX}"/usr/lib/python*/{_sysconfigdata*.py,config*/Makefile}
# Update make.conf with the new CHOST. We do this before rebuilding
# Perl below because Perl is a prime candidate for package blockers,
# which you are free to resolve manually at this point.
sed -i "/CHOST=/s/${OLD_CHOST}/${CHOST}/" "${EPREFIX}"/etc/portage/make.conf
# Perl is sensitive to a CHOST change so rebuild.
if portageq has_version "${EPREFIX}"/ dev-lang/perl && ! atom_new_chost "${PERL_PF}"; then
emerge -v1 "=${PERL_PF}"
fi
echo "All done! :)"
[[ -d "${EPREFIX}/usr/${OLD_CHOST}" ]] &&
echo "Tried to delete ${EPREFIX}/usr/${OLD_CHOST} but it wasn't empty. Please check and delete it manually."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment