#!/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.9"
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 3.4"
PYTHON_VERSIONS="${PYTHON2_VERSIONS} ${PYTHON3_VERSIONS}"

SUPPORTED_PMS="portage pkgcore paludis"
PMS_COMMAND=( "emerge" "pmerge" "cave" )
PMS_OPTIONS=( "-Dv1 --keep-going" "-Do" "resolve --execute --preserve-world" )
PMS_PRETENDING_OPTIONS=( "-p" "-p" "--no-execute" )
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 <<EOF_USAGE
${0##*/} -- Find & rebuild packages broken due to a Python upgrade

Usage: python-updater [OPTION]

Options:
    -h, --help      Print usage
    -V, --version   Print version
    -p, --pretend   Pretend (don't do anything)
    -v, --verbose   Increase verbosity (may be specified multiple times)
    --reinstall-identical-versions
                    Reinstall identical versions of packages
    -P PM, --package-manager PM
                    Use package manager PM, where PM can be one of:
$(for p in ${SUPPORTED_PMS}; do
echo -ne $'\t\t    '\* ${p}
if [[ ${p} == portage ]]; then
	echo ' (Default)'
else
	echo
fi
done)
    -c CMD, --command CMD
                    Pipe found packages to command CMD instead of invoking package
                    manager. Only for debug and script use.
    --package-manager-command CMD
                    Call CMD instead of the default command for the specified
                    package manager.
    -eCHECK --enable-CHECK
                    Enable CHECK where CHECK can be one of:
                    * pylibdir       (Enabled by default)
                    * PYTHON_ABIS    (Enabled by default)
                    * shared_linking (Enabled by default)
                    * static_linking (Enabled by default)
                    * manual         (Enabled by default)
                    * need_rebuild   (Enabled by default)
    -dCHECK --disable-CHECK
                    Disable CHECK. See --enable option for a list of checks
    -- OPTIONS      Pass additional options to package manager

See CHECKS section in the manpage for explanations about checks and
EXAMPLES section to learn how to use them.
EOF_USAGE
}

# veinfo(verbosity, message)
# einfo message if VERBOSE is bigger than verbosity.
veinfo() {
	if [[ VERBOSE -ge ${1} ]]; then
		shift
		einfo "${@}"
	fi
}

# die(message)
# Print error message and exit.
die() {
	eerror "${@}"
	exit 1
}

# has_version(cpv)
has_version() {
	if [[ "${PMS_COMMAND[${PMS_INDEX}]}" == "emerge" ]]; then
		/usr/bin/portageq has_version / "${1}"
	elif [[ "${PMS_COMMAND[${PMS_INDEX}]}" == "pmerge" ]]; then
		pquery --vdb "${1}"
	else
		cave has-version "${1}"
	fi
}

# get_vdb_variable(variable, environment_file)
get_vdb_variable() {
	local variable="${1}"
	local environment_file="${2}"

	unset "${variable}"
	local output
	output="$(bzip2 -cd "${environment_file}" | grep -E "^declare (-[[:alnum:]-]+ )?${variable}=")"
	if [[ -z "${output}" ]]; then
		output="$(bzip2 -cd "${environment_file}" | grep -E "^(declare (-[[:alnum:]-]+ )?)?${variable}=")"
	fi
	if [[ -n "${output}" ]]; then
		output="$(echo "${output}" | sed -e "s/^\(declare \(-[[:alnum:]-]\+ \)\?\)\?${variable}=\(\"\)\?//;s/\(\"\)\?$//")"
		if [[ "${output}" == "'"*"'" ]]; then
			output="${output:1:$((${#output}-2))}"
		fi
		eval "${variable}=\"\${output}\""
		return 0
	else
		return 1
	fi
}

# set_unless_changed(variable=value)
# This function can be used in /etc/portage/env.
set_unless_changed() {
	if [[ $# -lt 1 ]]; then
		die "${FUNCNAME}() requires at least 1 argument: VARIABLE=VALUE"
	fi

	local argument value variable
	for argument in "$@"; do
		if [[ ${argument} != *=* ]]; then
			die "${FUNCNAME}(): Argument '${argument}' has incorrect syntax"
		fi
		variable="${argument%%=*}"
		value="${argument#*=}"
		if eval "[[ \${${variable}} == \$(env -u ${variable} /usr/bin/portageq envvar ${variable}) ]]"; then
			eval "${variable}=\"\${value}\""
		fi
	done
}

# unset_unless_changed(variable)
# This function can be used in /etc/portage/env.
unset_unless_changed() {
	if [[ $# -lt 1 ]]; then
		die "${FUNCNAME}() requires at least 1 argument: VARIABLE"
	fi

	local variable
	for variable in "$@"; do
		if eval "[[ \${${variable}} == \$(env -u ${variable} /usr/bin/portageq envvar ${variable}) ]]"; then
			unset ${variable}
		fi
	done
}

global_USE_PYTHON=""

# get_USE_PYTHON(CATEGORY, PN, SLOT)
get_USE_PYTHON() {
	if [[ "${PMS_COMMAND[${PMS_INDEX}]}" =~ ^(emerge|pmerge)$ ]]; then
		if [[ -z "${global_USE_PYTHON}" ]]; then
			global_USE_PYTHON="$(/usr/bin/portageq envvar USE_PYTHON)"
		fi
		USE_PYTHON="${global_USE_PYTHON}"
		if [[ -f "/etc/portage/env/${1}/${2}" ]]; then
			USE_PYTHON="$(. "/etc/portage/env/${1}/${2}"; echo "${USE_PYTHON}")"
		fi
		if [[ -f "/etc/portage/env/${1}/${2}:${3}" ]]; then
			USE_PYTHON="$(. "/etc/portage/env/${1}/${2}:${3}"; echo "${USE_PYTHON}")"
		fi
	else
		USE_PYTHON="$(cave print-id-environment-variable "${1}/${2}${3:+:}${3}::/?" --best --format %v --variable-name USE_PYTHON)"
	fi
	if [[ -z "${USE_PYTHON}" ]]; then
		USE_PYTHON="${NEW_PYTHON2_VERSION} ${NEW_PYTHON3_VERSION}"
		USE_PYTHON="${USE_PYTHON# }"
		USE_PYTHON="${USE_PYTHON% }"
	fi
	echo "${USE_PYTHON}"
}

# get_ebuild_path(CATEGORY, PN, SLOT)
get_ebuild_path() {
	# These manual calls to Portage API are 2 times faster than 'portageq get_repo_path / "$(portageq metadata / ebuild "$(portageq best_visible / "${1}")" repository)"'.
	/usr/bin/python -c "
import sys

if '' in sys.path:
	sys.path.remove('')

import portage

dbapi = portage.db['/']['porttree'].dbapi
pkg_list = dbapi.match('${1}/${2}${3:+:}${3}')
if pkg_list:
	best_visible = portage.best(pkg_list)
	if best_visible:
		repository = dbapi.aux_get(best_visible, ['repository'])
		if repository:
			repository_path = dbapi.getRepositoryPath(repository[0])
			if repository_path:
				print(repository_path + '/${1}/${2}/' + best_visible.split('/')[1] + '.ebuild')"
}

# get_RESTRICT_PYTHON_ABIS(CATEGORY, PN, SLOT)
get_RESTRICT_PYTHON_ABIS() {
	if [[ "${PMS_COMMAND[${PMS_INDEX}]}" =~ ^(emerge|pmerge)$ ]]; then
		local ebuild_path="$(get_ebuild_path "${1}" "${2}" "${3}")"
		if [[ -f "${ebuild_path}" ]]; then
			grep "^RESTRICT_PYTHON_ABIS=" "${ebuild_path}" | sed -e "s/RESTRICT_PYTHON_ABIS=[\"']\(.*\)[\"']/\1/"
		fi
	else
		cave print-id-environment-variable "${1}/${2}${3:+:}${3}::/?" --best --format %v --variable-name RESTRICT_PYTHON_ABIS
	fi
}

# check_python_abi_matching(PYTHON_ABI, PYTHON_ABI_pattern)
check_python_abi_matching() {
	if [[ "$2" == *"-cpython" ]]; then
		[[ "$1" =~ ^[[:digit:]]+\.[[:digit:]]+$ && "$1" == ${2%-cpython} ]]
	elif [[ "$2" == *"-jython" ]]; then
		[[ "$1" == $2 ]]
	else
		if [[ "$1" =~ ^[[:digit:]]+\.[[:digit:]]+$ ]]; then
			[[ "$1" == $2 ]]
		elif [[ "$1" =~ ^[[:digit:]]+\.[[:digit:]]+-jython$ ]]; then
			[[ "${1%-jython}" == $2 ]]
		else
			die "Unrecognized Python ABI '$1'"
		fi
	fi
}

# get_OLD_PYTHON_VERSIONS_REGEX()
get_OLD_PYTHON_VERSIONS_REGEX() {
	if [[ -n "${PYTHON_REQUESTED_ACTIVE_VERSION}" ]]; then
		if [[ "${PYTHON_REQUESTED_ACTIVE_VERSION}" == "2" ]]; then
			echo "${OLD_PYTHON2_VERSIONS_REGEX}"
		elif [[ "${PYTHON_REQUESTED_ACTIVE_VERSION}" == "3" ]]; then
			echo "${OLD_PYTHON3_VERSIONS_REGEX}"
		fi
	else
		echo "${OLD_PYTHON_VERSIONS_REGEX}"
	fi
}

# get_OLD_PYTHON_SHARED_LIBRARIES_REGEX()
get_OLD_PYTHON_SHARED_LIBRARIES_REGEX() {
	if [[ -n "${PYTHON_REQUESTED_ACTIVE_VERSION}" ]]; then
		if [[ "${PYTHON_REQUESTED_ACTIVE_VERSION}" == "2" ]]; then
			echo "${OLD_PYTHON2_SHARED_LIBRARIES_REGEX}"
		elif [[ "${PYTHON_REQUESTED_ACTIVE_VERSION}" == "3" ]]; then
			echo "${OLD_PYTHON3_SHARED_LIBRARIES_REGEX}"
		fi
	else
		echo "${OLD_PYTHON_SHARED_LIBRARIES_REGEX}"
	fi
}

# Respect PYUPDATER_OPTIONS
if [[ -n "${PYUPDATER_OPTIONS}" ]]; then
	set -- ${PYUPDATER_OPTIONS} "${@}"
fi

# Respect PACKAGE_MANAGER environment variable
i=0
for pm in ${SUPPORTED_PMS}; do
	if [[ "${pm}" == "${PACKAGE_MANAGER}" ]]; then
		PMS_INDEX="${i}"
		break
	fi
	((i++))
done

# Command Line Parsing
while [[ -n "${1}" ]]; do
	case "${1}" in
		-h|--help)
			usage
			exit 0
			;;
		-V|--version)
			echo "${VERSION}"
			exit 0
			;;
		-p|--pretend)
			PRETEND=1
			;;
		-v|--verbose)
			((VERBOSE++))
			;;
		--reinstall-identical-versions)
			REINSTALL_IDENTICAL_VERSIONS=1
			;;
		-P|--package-manager)
			shift
			PACKAGE_MANAGER="${1}"
			case "${PACKAGE_MANAGER}" in
				portage|pkgcore|paludis)
					;;
				*)
					echo "Unrecognized package manager selected. Please select between ${SUPPORTED_PMS}"
					exit 1
					;;
			esac

			# PMS_INDEX is used to select the right commands and options for the selected package manager
			PMS_INDEX=0
			for PM in ${SUPPORTED_PMS}; do
				[[ ${PM} == ${PACKAGE_MANAGER} ]] && break
				PMS_INDEX=$((${PMS_INDEX} + 1))
			done
			;;
		--package-manager-command)
			shift
			CUSTOM_PMS_COMMAND="${1}"
			;;
		-c|--command)
			shift
			PIPE_COMMAND="${1}"
			;;
		-emanual|--enable-manual)
			CHECK_MANUAL=1
			;;
		-dmanual|--disable-manual)
			CHECK_MANUAL=0
			;;
		-ePYTHON_ABIS|--enable-PYTHON_ABIS)
			CHECK_PYTHON_ABIS=1
			;;
		-dPYTHON_ABIS|--disable-PYTHON_ABIS)
			CHECK_PYTHON_ABIS=0
			;;
		-epylibdir|--enable-pylibdir)
			CHECK_PYLIBDIR=1
			;;
		-dpylibdir|--disable-pylibdir)
			CHECK_PYLIBDIR=0
			;;
		-eshared_linking|--enable-shared_linking)
			CHECK_SHARED_LINKING=1
			;;
		-dshared_linking|--disable-shared_linking)
			CHECK_SHARED_LINKING=0
			;;
		-estatic_linking|--enable-static_linking)
			CHECK_STATIC_LINKING=1
			;;
		-dstatic_linking|--disable-static_linking)
			CHECK_STATIC_LINKING=0
			;;
		-eneed_rebuild|--enable-need_rebuild)
			CHECK_ECLASS_NEED_REBUILD=1
			;;
		-dneed_rebuild|--disable-need_rebuild)
			CHECK_ECLASS_NEED_REBUILD=0
			;;
		--)
			shift
			ADDITIONAL_OPTIONS="${*}"
			break
			;;
		*)
			usage
			echo "Unrecognized option '${1}'"
			exit 1
			;;
	esac
	shift
done

if has_version "=dev-lang/python-2*"; then
	if [[ "$(readlink /usr/bin/python2)" != "python2."* ]]; then
		die "'/usr/bin/python2' is not valid symlink"
	fi
	NEW_PYTHON2_VERSION="$(/usr/bin/python2 -c 'from sys import version_info; print(".".join([str(x) for x in version_info[:2]]))')"
fi
if has_version "=dev-lang/python-3*"; then
	if [[ "$(readlink /usr/bin/python3)" != "python3."* ]]; then
		die "'/usr/bin/python3' is not valid symlink"
	fi
	NEW_PYTHON3_VERSION="$(/usr/bin/python3 -c 'from sys import version_info; print(".".join([str(x) for x in version_info[:2]]))')"
fi
if [[ -z "${NEW_PYTHON2_VERSION}" && -z "${NEW_PYTHON3_VERSION}" ]]; then
	die "Python 2 and Python 3 not installed"
fi

OLD_PYTHON_SHARED_LIBRARIES_REGEX=""
for python_version in ${PYTHON_VERSIONS}; do
	if [[ "${python_version}" != "${NEW_PYTHON_VERSION}" ]]; then
		OLD_PYTHON_VERSIONS+="${OLD_PYTHON_VERSIONS:+ }${python_version}"
		soname="$(readlink -n /usr/lib/libpython${python_version}.so)"
		if [[ -z "${soname}" ]]; then
			# Use relatively safe, default value.
			soname="libpython${python_version}.so.1.0"
		fi
		OLD_PYTHON_SHARED_LIBRARIES_REGEX+="${OLD_PYTHON_SHARED_LIBRARIES_REGEX:+|}${soname//./\.}"
	fi
done
OLD_PYTHON_VERSIONS_REGEX="(${OLD_PYTHON_VERSIONS// /|})"
OLD_PYTHON_VERSIONS_REGEX="${OLD_PYTHON_VERSIONS_REGEX//./\.}"
OLD_PYTHON_SHARED_LIBRARIES_REGEX="(${OLD_PYTHON_SHARED_LIBRARIES_REGEX})"

OLD_PYTHON2_SHARED_LIBRARIES_REGEX=""
for python_version in ${PYTHON2_VERSIONS}; do
	if [[ "${python_version}" != "${NEW_PYTHON2_VERSION}" ]]; then
		OLD_PYTHON2_VERSIONS+="${OLD_PYTHON2_VERSIONS:+ }${python_version}"
		soname="$(readlink -n /usr/lib/libpython${python_version}.so)"
		if [[ -z "${soname}" ]]; then
			# Use relatively safe, default value.
			soname="libpython${python_version}.so.1.0"
		fi
		OLD_PYTHON2_SHARED_LIBRARIES_REGEX+="${OLD_PYTHON2_SHARED_LIBRARIES_REGEX:+|}${soname//./\.}"
	fi
done
OLD_PYTHON2_VERSIONS_REGEX="(${OLD_PYTHON2_VERSIONS// /|})"
OLD_PYTHON2_VERSIONS_REGEX="${OLD_PYTHON2_VERSIONS_REGEX//./\.}"
OLD_PYTHON2_SHARED_LIBRARIES_REGEX="(${OLD_PYTHON2_SHARED_LIBRARIES_REGEX})"

OLD_PYTHON3_SHARED_LIBRARIES_REGEX=""
for python_version in ${PYTHON3_VERSIONS}; do
	if [[ "${python_version}" != "${NEW_PYTHON3_VERSION}" ]]; then
		OLD_PYTHON3_VERSIONS+="${OLD_PYTHON3_VERSIONS:+ }${python_version}"
		soname="$(readlink -n /usr/lib/libpython${python_version}.so)"
		if [[ -z "${soname}" ]]; then
			# Use relatively safe, default value.
			soname="libpython${python_version}.so.1.0"
		fi
		OLD_PYTHON3_SHARED_LIBRARIES_REGEX+="${OLD_PYTHON3_SHARED_LIBRARIES_REGEX:+|}${soname//./\.}"
	fi
done
OLD_PYTHON3_VERSIONS_REGEX="(${OLD_PYTHON3_VERSIONS// /|})"
OLD_PYTHON3_VERSIONS_REGEX="${OLD_PYTHON3_VERSIONS_REGEX//./\.}"
OLD_PYTHON3_SHARED_LIBRARIES_REGEX="(${OLD_PYTHON3_SHARED_LIBRARIES_REGEX})"

einfo $'\e[1;34mStarting Python Updater...\e[0m'
einfo $'\e[1;36m'"Main active version of Python:  ${NEW_PYTHON_VERSION}"$'\e[0m'
einfo $'\e[1;36m'"Active version of Python 2:     ${NEW_PYTHON2_VERSION:-(None)}"$'\e[0m'
einfo $'\e[1;36m'"Active version of Python 3:     ${NEW_PYTHON3_VERSION:-(None)}"$'\e[0m'

if [[ CHECK_SHARED_LINKING -ne 0 ]]; then
	if ! type -P scanelf >/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 "Missing metadata in '${environment_file}' file. Manually reinstall corresponding package."
	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_PRETENDING_OPTIONS[${PMS_INDEX}]}"

# (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
