#!/bin/bash # vim: set et sw=4 sts=4 tw=80: # Copyright 2007-2011 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # A bit of hackery to update everything that is humanly possible # that maybe related to an older version of Python. This script can # be run as many times as you like. # # OLD_PYTHON_VERSIONS = Old Python versions we are upgrading from. # NEW_PYTHON_VERSION = New Python version we are upgrading to. # PKGS_EXCEPTIONS = Packages that should NOT be re-emerged for any reason. # PKGS_MANUAL = Packages that should be re-emerged even if they don't fit # the criteria (eg. ones that have Python compiled statically). # # Runtime Variables: # # PKGS_TO_REMERGE = List of packages we deem to need re-emerging. # PKGS_OK = List of packages that should be merged without any problems. # PKGS_MISSING = List of packages that are installed, but cannot be merged, # because they have been pruned from portage. VERSION="0.8" OLD_PYTHON_VERSIONS="" OLD_PYTHON2_VERSIONS="" OLD_PYTHON3_VERSIONS="" NEW_PYTHON_VERSION="$(/usr/bin/python -c 'from sys import version_info; print(".".join([str(x) for x in version_info[:2]]))')" NEW_PYTHON2_VERSION="" NEW_PYTHON3_VERSION="" PKGS_EXCEPTIONS="dev-lang/python sys-apps/portage" PKGS_MANUAL="" PRETEND=0 REINSTALL_IDENTICAL_VERSIONS=0 VERBOSE=0 PKGS_TO_REMERGE="" PKGS_COUNT_REMERGE=0 PYTHON2_VERSIONS="2.1 2.2 2.3 2.4 2.5 2.6 2.7" PYTHON3_VERSIONS="3.0 3.1 3.2 3.3" PYTHON_VERSIONS="${PYTHON2_VERSIONS} ${PYTHON3_VERSIONS}" SUPPORTED_PMS="portage pkgcore paludis" PMS_COMMAND=( "emerge" "pmerge" "paludis" ) PMS_OPTIONS=( "-Dv1 --keep-going" "-Do" "-i1" ) PMS_INDEX=0 CUSTOM_PMS_COMMAND="" ADDITIONAL_OPTIONS="" # Checks CHECK_MANUAL=1 CHECK_PYLIBDIR=1 CHECK_PYTHON_ABIS=1 CHECK_SHARED_LINKING=1 CHECK_STATIC_LINKING=1 CHECK_ECLASS_NEED_REBUILD=1 # load the gentoo-style info macros, but hack to get around # it thinking this is an rc script EBUILD="1" source /etc/init.d/functions.sh # portage variables PKG_DBDIR=/var/db/pkg # usage() # display usage usage() { cat </dev/null 2>&1; then ewarn "scanelf not found!" ewarn "check shared_linking is disabled." CHECK_SHARED_LINKING=0 else veinfo 1 'check "shared_linking" enabled.' fi else veinfo 1 'check "shared_linking" disabled.' fi if [[ CHECK_STATIC_LINKING -ne 0 ]]; then if ! type -P scanelf >/dev/null 2>&1; then ewarn "scanelf not found!" ewarn "check static_linking is disabled." CHECK_STATIC_LINKING=0 else veinfo 1 'check "static_linking" enabled.' fi else veinfo 1 'check "static_linking" disabled.' fi [[ CHECK_PYLIBDIR -ne 0 ]] \ && veinfo 1 'check "pylibdir" enabled.' \ || veinfo 1 'check "pylibdir" disabled.' [[ CHECK_MANUAL -ne 0 ]] \ && veinfo 1 'check "manual" enabled.' \ || veinfo 1 'check "manual" disabled.' [[ CHECK_ECLASS_NEED_REBUILD -ne 0 ]] \ && veinfo 1 'check "need_rebuild" enabled.' \ || veinfo 1 'check "need_rebuild" disabled.' # Iterate through the contents of all the installed packages. # ${PKG_DBDIR} must be followed by '/' to avoid problems when ${PKG_DBDIR} is a symlink. for content in $(find ${PKG_DBDIR}/ -name CONTENTS | sort); do environment_file="${content/CONTENTS/environment.bz2}" # Extract some variables. SUPPORT_PYTHON_ABIS, PYTHON_ABIS and PYTHON_REQUESTED_ACTIVE_VERSION are optional. get_vdb_variable PVR "${environment_file}" || die "PVR missing" get_vdb_variable SUPPORT_PYTHON_ABIS "${environment_file}" get_vdb_variable PYTHON_ABIS "${environment_file}" get_vdb_variable PYTHON_REQUESTED_ACTIVE_VERSION "${environment_file}" # Manually calculate CATEGORY, PF, PN and SLOT to avoid problems with moved packages. CATEGORY="$(echo "${environment_file#${PKG_DBDIR}/}" | sed -e "s:/.*::")" PF="$(echo "${environment_file#${PKG_DBDIR}/${CATEGORY}/}" | sed -e "s:/.*::")" PN="${PF%-${PVR}}" if [[ -f "${content/CONTENTS/SLOT}" ]]; then SLOT="$(<"${content/CONTENTS/SLOT}")" else get_vdb_variable SLOT "${environment_file}" fi CATPKG="${CATEGORY}/${PN}" CATPKGVER="${CATEGORY}/${PF}" veinfo 2 "Checking ${CATEGORY}/${PF}${SLOT:+:}${SLOT}" # Exclude packages that are exceptions, like Portage and Python itself. exception=0 for exp in ${PKGS_EXCEPTIONS}; do if [[ -z "${CATPKG##${exp}}" ]]; then veinfo 2 "Skipping ${CATPKG}, reason: exception" exception=1 break; fi done [[ ${exception} == 1 ]] && continue # Check if package is in PKGS_MANUAL if [[ CHECK_MANUAL -ne 0 ]]; then for pkg in ${PKGS_MANUAL}; do if [[ -z "${CATPKG##${pkg}}" ]]; then exception=2 break; fi done fi # Replace SLOT by version number when REINSTALL_IDENTICAL_VERSIONS == 1 # Reinstall identical versions when SLOT doesn't exist, bug #201848 if [[ REINSTALL_IDENTICAL_VERSIONS -eq 1 || -z "${SLOT}" ]]; then CATPKGVER="=${CATPKGVER}" else CATPKGVER="${CATPKG}:${SLOT}" fi if [[ ${exception} = 2 ]]; then PKGS_TO_REMERGE+=" ${CATPKGVER}" eindent einfo "Adding to list: ${CATPKGVER}" eindent einfo "check: manual [Added to list manually, see CHECKS in manpage for more information.]" eoutdent && eoutdent continue fi if [[ CHECK_PYTHON_ABIS -ne 0 ]]; then if [[ -n "${SUPPORT_PYTHON_ABIS}" ]]; then new_PYTHON_ABIS="" RESTRICT_PYTHON_ABIS="$(get_RESTRICT_PYTHON_ABIS "${CATEGORY}" "${PN}" "${SLOT}")" USE_PYTHON="$(get_USE_PYTHON "${CATEGORY}" "${PN}" "${SLOT}")" for PYTHON_ABI in ${USE_PYTHON}; do support_ABI="1" for restricted_ABI in ${RESTRICT_PYTHON_ABIS}; do if check_python_abi_matching "${PYTHON_ABI}" "${restricted_ABI}"; then support_ABI="0" break fi done [[ "${support_ABI}" == "1" ]] && new_PYTHON_ABIS+="${new_PYTHON_ABIS:+ }${PYTHON_ABI}" done eindent && eindent veinfo 3 "Requested ABIs: \"${USE_PYTHON}\"" veinfo 3 "Restricted ABIs: \"${RESTRICT_PYTHON_ABIS}\"" veinfo 3 "Previously enabled ABIs: \"${PYTHON_ABIS}\"" veinfo 3 "Newly enabled ABIs: \"${new_PYTHON_ABIS}\"" eoutdent && eoutdent if [[ "${PYTHON_ABIS}" != "${new_PYTHON_ABIS}" ]]; then PKGS_TO_REMERGE+=" ${CATPKGVER}" eindent einfo "Adding to list: ${CATPKGVER}" eindent veinfo 1 "check: PYTHON_ABIS [ Previous Python ABIs: ${PYTHON_ABIS}, new Python ABIs: ${new_PYTHON_ABIS} ]" eoutdent && eoutdent continue fi # Don't run other checks if PYTHON_ABIS check has been run. continue fi fi if [[ CHECK_STATIC_LINKING -ne 0 ]]; then binaries="$(scanelf -qs +Py_Initialize < <(grep -E "^obj" "${content}" | cut -d" " -f2 | grep -Ev "^/usr/lib(32|64)?/debug/") | sed "s/.* //")" if [[ -n "${binaries}" ]]; then PKGS_TO_REMERGE+=" ${CATPKGVER}" eindent einfo "Adding to list: ${CATPKGVER}" eindent veinfo 1 "check: static_linking [ Binaries linked against Python static libraries found:" eindent old_IFS="${IFS}" IFS=$'\n' for binary in ${binaries}; do veinfo 1 "${binary}" done IFS="${old_IFS}" eoutdent veinfo 1 "]" eoutdent && eoutdent fi fi if [[ "${PYTHON_REQUESTED_ACTIVE_VERSION}" =~ ^[[:digit:]]+\.[[:digit:]]+$ ]]; then eindent && eindent veinfo 2 "Requested active version of Python: \"${PYTHON_REQUESTED_ACTIVE_VERSION}\"" eoutdent && eoutdent # Don't run other checks if given package has been built with precisely specified requested active version of Python. continue fi if [[ CHECK_ECLASS_NEED_REBUILD -ne 0 ]]; then get_vdb_variable PYTHON_NEED_REBUILD "${environment_file}" if echo "${PYTHON_NEED_REBUILD}" | grep -qE "$(get_OLD_PYTHON_VERSIONS_REGEX)"; then PKGS_TO_REMERGE+=" ${CATPKGVER}" eindent einfo "Adding to list: ${CATPKGVER}" eindent veinfo 1 "check: need_rebuild [ Ebuild set PYTHON_NEED_REBUILD=${PYTHON_NEED_REBUILD} ]" eoutdent && eoutdent continue fi fi if [[ CHECK_PYLIBDIR -ne 0 ]]; then # Search for possible old Python dirs in CONTENTS # /usr/include/python$old # /usr/lib/python$old # /usr/lib32/python$old # /usr/lib64/python$old if grep -qE "/usr/(include|lib(32|64)?)/python$(get_OLD_PYTHON_VERSIONS_REGEX)" "${content}"; then PKGS_TO_REMERGE+=" ${CATPKGVER}" eindent einfo "Adding to list: ${CATPKGVER}" eindent veinfo 1 "check: pylibdir [ Installed file under old Python include/library directory ]" eoutdent && eoutdent continue fi fi if [[ CHECK_SHARED_LINKING -ne 0 ]]; then binaries="$(scanelf -qF "%F %n" < <(grep -E "^obj" "${content}" | cut -d" " -f2 | grep -Ev "^/usr/lib(32|64)?/debug/") | grep -E "( |,)$(get_OLD_PYTHON_SHARED_LIBRARIES_REGEX)(,|$)")" if [[ -n "${binaries}" ]]; then PKGS_TO_REMERGE+=" ${CATPKGVER}" eindent einfo "Adding to list: ${CATPKGVER}" eindent veinfo 1 "check: shared_linking [ Binaries linked against old Python shared libraries found:" eindent old_IFS="${IFS}" IFS=$'\n' for binary in ${binaries}; do veinfo 1 "${binary}" done IFS="${old_IFS}" eoutdent veinfo 1 "]" eoutdent && eoutdent fi fi done # Pipe to command if we have one if [[ -n "${PIPE_COMMAND}" ]]; then echo "${PKGS_TO_REMERGE}" | ${PIPE_COMMAND} exit "${?}" fi if [[ "${PMS_COMMAND[${PMS_INDEX}]}" == "emerge" ]] ; then # Filter out --getbinpkg, --getbinpkgonly, --usepkg and --usepkgonly options in EMERGE_DEFAULT_OPTS environment variable emerge_default_opts="" for option in $(/usr/bin/portageq envvar EMERGE_DEFAULT_OPTS); do if [[ "${option}" == -[[:alnum:]]* ]]; then [[ "${option//[gGkK]/}" != "-" ]] && emerge_default_opts+=" ${option//[gGkK]/}" elif [[ "${option}" != "--getbinpkg" && "${option}" != "--getbinpkgonly" && "${option}" != "--usepkg" && "${option}" != "--usepkgonly" ]]; then emerge_default_opts+=" ${option}" fi done export EMERGE_DEFAULT_OPTS="${emerge_default_opts# }" fi # Only pretending? [[ PRETEND -eq 1 ]] && PMS_OPTIONS[${PMS_INDEX}]="${PMS_OPTIONS[${PMS_INDEX}]} -p" # (Pretend to) reinstall packages if [[ -n "${PKGS_TO_REMERGE}" ]]; then pmscmd="${CUSTOM_PMS_COMMAND}" [[ -z "${pmscmd}" ]] && pmscmd="${PMS_COMMAND[${PMS_INDEX}]}" cmd="${pmscmd} ${PMS_OPTIONS[${PMS_INDEX}]} ${PKGS_TO_REMERGE} ${ADDITIONAL_OPTIONS}" einfo ${cmd} ${cmd} else einfo "No packages need to be reinstalled." fi