#!/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."