#!/bin/bash # Copyright 1999-2007 Gentoo Foundation # revdep-rebuild: Reverse dependency rebuilder. # Original Author: Stanislav Brabec # Current Maintainer: Paul Varner # Print pre-release disclaimer to stderr cat 1>&2 << EOF WARNING WARNING *** This is a rewritten version of revdep-rebuild *** WARNING WARNING WARNING Please report any bugs to http://bugs.gentoo.org WARNING WARNING In the bug report please include the following information: WARNING emerge --info WARNING A copy of the output from the revdep-rebuild command WARNING A copy of the .revdep-rebuild* files as an attachment WARNING WARNING If the bug is severe, the previous version of revdep-rebuild is located WARNING at: /usr/lib/gentoolkit/bin/revdep-rebuild WARNING WARNING WARNING *** This is a rewritten version of revdep-rebuild *** WARNING EOF sleep 2 # End pre-release disclaimer to stderr source /etc/init.d/functions.sh # TODO: # - Use more /etc/init.d/functions.sh # Customizable variables: # # LD_LIBRARY_MASK - Mask of specially evaluated libraries # SEARCH_DIRS - List of directories to search for executables and libraries # SEARCH_DIRS_MASK - List of directories to not search # # These variables can be prepended to either by setting the variable in # your environment prior to execution, or by placing an entry in # /etc/make.conf. # # An entry of "-*" means to clear the variable from that point forward. # Example: env SEARCH_DIRS="/usr/bin -*" revdep-rebuild will set SEARCH_DIRS # to contain only /usr/bin declare -r oIFS="$IFS" rm() { [[ $LIST && $appname ]] || die 1 '$LIST or $appname is not defined! (This is a bug.)' for i in $@; do [[ $i = -* || $i = *.$appname* ]] || die 1 "Oops, I'm not allowed to delete that. ($@)" done command rm "$@" } # Somewhat more portable find -executable # FIXME/UNTESTED (I don't have access to all of the different versions of # find.) # Usage: find PATH ARGS -- use find like normal, except use -executable instead # of various versions of -perm /+ blah blah and hacks find() { hash find || { die 1 'find not found!'; } # We can be pretty sure "$0" should be executable. if [[ $(command find "$0" -executable 2> /dev/null) ]]; then unset -f find # We can just use the command find elif [[ $(command find "$0" -perm /u+x 2> /dev/null) ]]; then find() { a=(${@//-executable/-perm \/u+x}) a=(${a//-writable/-perm \/u+w}) a=(${a//-readable/-perm \/r+w}) command find "${a[@]}" } elif [[ $(command find "$0" -perm +u+x 2> /dev/null) ]]; then find() { a=(${@//-executable/-perm +u+x}) a=(${a//-writable/-perm +u+w}) a=(${a//-readable/-perm +r+w}) command find "${a[@]}" } else # Last resort find() { a=(${@//-executable/-exec test -x '{}' \;}) a=(${a//-writable/-exec test -w '{}' \;}) a=(${a//-readable/-exec test -r '{}' \;}) command find "${a[@]}" } fi find "$@" } print_usage() { cat << EOF Usage: $0 [OPTIONS] [--] [EMERGE_OPTIONS] Broken reverse dependency rebuilder. -h, --help Print this usage -k, --keep-temp Do not delete temporary files on exit -e, --exact Emerge based on exact package version -l, --no-ld-path Do not set LD_LIBRARY_PATH -C, --nocolor Turn off colored output -i, --ignore Ignore temporary files from previous runs -o, --no-order Do not check the build order (Saves time, but may cause breakage.) -q, --quiet Be less verbose (also passed to emerge command) -v, --verbose Be more verbose -u, --no-util UTIL Do not use features provided by UTIL --no-util=UTIL UTIL can be one of portage-utils or pkgcore or it can be a *quoted* space-delimited list. -L, --library NAME Emerge existing packages that use the library with NAME --library=NAME NAME can be a full path to the library or a basic regular expression (man grep) Calls emerge, all other options are used for it (e. g. -p, --pretend). Report bugs to EOF } # Usage: progress i n # i: current item # n: total number of items to process progress() { if [[ $quiet ]]; then progress() { :; } else progress() { (( $1 == $2 )) && local lb=$'\n' echo -ne '\r \r' echo -n "[ $(( $1 * 100 / $2 ))% ] $lb" } progress $@ fi } # Get the name of a package owning a file on the filesystem using one of several # utilities: This is a placeholder. The function is defined in get_args() get_file_owner() { :; } # Replace whitespace with linebreaks, normalize repeated '/' chars, and sort -u # (If any libs have whitespace in their filenames, someone needs punishment.) clean_var() { local a=$(echo ${@%%-\**}) # Deliberately unquoted # A benchmark shows this loop is faster than piping to sed, # as long as there aren't more than a handful of '/' chars. while [[ $a = *//* ]]; do a="${a//\/\///}"; done sort -u <<< "${a// /$'\n'}" } # Exit and optionally output to sterr die() { local status=$1 shift eerror "$@" exit $status } # What to do when dynamic linking is consistent clean_exit() { [[ $KEEP_TEMP ]] || rm $LIST.?_* echo einfo "$OK_TEXT... All done. " exit 0 } get_args() { appname="${0##*/}" echo_v() { ewarn "$@"; } unset VERBOSE KEEP_TEMP EMERGE_OPTIONS remove_old_tempfiles order_packages=1 PACKAGE_NAMES=1 SONAME="not found" SEARCH_BROKEN=1 FULL_LD_PATH=1 local avoid_utils while [[ $1 ]]; do case $1 in -h|--help) print_usage exit 0 ;; -e|--exact) unset PACKAGE_NAMES ;; -o|--no-order) unset order_packages ;; -q|--quiet) echo_v() { : ; } quiet=1 EMERGE_OPTIONS+=($1) ;; -L=*|--library=*|--soname=*|--soname-regexp=*) SONAME="${1#*=}" unset SEARCH_BROKEN ;; -L|--library|--soname|--soname-regexp) [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1" shift SONAME="$1" unset SEARCH_BROKEN ;; -u=*|--no-util=*) # TODO: check for invalid values avoid_utils="${1#*=}" ;; -u|--no-util) [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1" shift avoid_utils="$1" ;; -nc|-C|--no-color|--nocolor) # TODO: Does this variable do anything now that the main color functions are removed? export NOCOLOR=1 ;; -l|-np|--no-ld-path) unset FULL_LD_PATH ;; -i|--ignore) remove_old_tempfiles=1 ;; -k|--keep-temp) KEEP_TEMP=1 ;; -vv|--extra-verbose|-v|--verbose) VERBOSE=1 EMERGE_OPTIONS+=($1) ;; --) ;; *) EMERGE_OPTIONS+=($1) ;; esac shift done # Check if various utils are allowed and installed if [[ $avoid_utils != *portage-utils* ]] && hash qfile 2> /dev/null; then get_file_owner() { qfile -qvC "$@"; } elif [[ $avoid_utils != *pkgcore* ]] && hash pquery 2> /dev/null; then get_file_owner() { local IFS=,; pquery --nocolor --owns="$*"; } # equery disabled for incompatibility with modern portage. # elif [[ $avoid_utils != *equery* ]] && hash equery 2> /dev/null; then # get_file_owner() { equery -q -C b $@; } else get_file_owner() { local IFS=$'\n' find /var/db/pkg -name CONTENTS -print0 | xargs -0 grep -Fl "$*" | sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:' } fi EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend}) EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly}) if [[ ${EMERGE_OPTIONS[@]} != *--pretend* && $UID -ne 0 ]]; then ewarn "You are not superuser. Adding --pretend to emerge options." EMERGE_OPTIONS+=(--pretend) fi } is_real_merge() [[ ${EMERGE_OPTIONS[@]} != *--pretend* && ${EMERGE_OPTIONS[@]} != *--fetchonly* ]] get_args "$@" einfo "Configuring search environment for $appname" # Obey PORTAGE_NICENESS PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS) if [[ $PORTAGE_NICENESS ]]; then renice $PORTAGE_NICENESS $$ > /dev/null # Since we have already set our nice value for our processes, # reset PORTAGE_NICENESS to zero to avoid having emerge renice again. export PORTAGE_NICENESS="0" fi PORTAGE_ROOT=$(portageq envvar ROOT) PORTAGE_ROOT="${PORTAGE_ROOT:-/}" # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf, # portage, and the environment # Read the incremental variables from environment and portage # Until such time as portage supports these variables as incrementals # The value will be what is in /etc/make.conf SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS) SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK) LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK) # Add the defaults if [[ -d /etc/revdep-rebuild ]]; then for file in /etc/revdep-rebuild/*; do SEARCH_DIRS+=" "$(. $file; echo $SEARCH_DIRS) SEARCH_DIRS_MASK+=" "$(. $file; echo $SEARCH_DIRS_MASK) LD_LIBRARY_MASK+=" "$(. $file; echo $LD_LIBRARY_MASK) done else SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*" SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice" LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so" fi # Get the ROOTPATH and PATH from /etc/profile.env if [[ -rs "/etc/profile.env" ]]; then SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH") fi # Get the directories from /etc/ld.so.conf if [[ -rs /etc/ld.so.conf ]]; then SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf) fi # Set the final variables [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug." SEARCH_DIRS=$(clean_var "$SEARCH_DIRS") SEARCH_DIRS_MASK=$(clean_var "$SEARCH_DIRS_MASK") LD_LIBRARY_MASK=$(clean_var "$LD_LIBRARY_MASK") # Use the color preference from portage NOCOLOR=$(portageq envvar NOCOLOR) # Find a place to put temporary files # TODO; let the user choose where to put tempfiles # gfind $HOME/ /var/tmp/ /tmp/ -writable -print -quit # TODO: This is a rather noisy, but portable way to implement -quit LIST=$( find $HOME/ /var/tmp/ /tmp/ -writable | while read LIST; do echo "$LIST.$appname" break done ) if [[ $LIST = .$appname ]]; then die 1 "!!! Unable to find a satisfactory location for temporary files !!!" fi [[ $remove_old_tempfiles ]] && rm -f $LIST.* function set_trap () { trap "rm_temp $1" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM } function rm_temp () { rm $1 die 1 $' ...terminated. Removing incomplete '"$1." } get_search_env() { if [[ $SEARCH_BROKEN ]]; then SONAME_SEARCH="$SONAME" HEAD_TEXT="broken by a package update" OK_TEXT="Dynamic linking on your system is consistent" WORKING_TEXT="consistency" else # first case is needed to test against /path/to/foo.so if [[ $SONAME = /* ]]; then # Set to "$SONAME" SONAME_SEARCH=" $SONAME " else # Set to "$SONAME" SONAME_SEARCH=$'\t'"$SONAME " fi local uuid="${SONAME##*/}" uuid="${uuid//[[:space:]]}" LIST+="_$uuid" HEAD_TEXT="using $SONAME" OK_TEXT="There are no dynamic links to $SONAME" unset WORKING_TEXT fi [[ $LIST ]] || die 1 $LIST IS NOT DEFINED # If any of our temporary files are older than 1 day, remove them all [[ ! $keep_tempfiles && -r $LIST && $( find -L "$LIST" -type f -mmin +1440 -print | while read; do echo 1; break; done ) ]] && rm -f $LIST.* # Compare old and new environments # Don't use our previous files if environment doesn't match new_env=$( # We don't care if these options change EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--pretend/}) EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--fetchonly/}) cat <<- EOF SEARCH_DIRS="$SEARCH_DIRS" SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK" LD_LIBRARY_MASK="$LD_LIBRARY_MASK" PORTAGE_ROOT="$PORTAGE_ROOT" EMERGE_OPTIONS="${EMERGE_OPTIONS[@]}" order_packages="$order_packages" FULL_LD_PATH="$FULL_LD_PATH" EOF ) if [[ -rs $LIST.0_env ]]; then old_env=$(<"$LIST.0_env") if [[ $old_env != $new_env ]]; then ewarn 'Environment mismatch from previous run, deleting temporary files...' rm -f "$LIST"* fi else # No 0_env file found, silently delete any other tempfiles that may exist rm -f "$LIST"* fi # Save the environment in a file for next time echo "$new_env" > "$LIST.0_env" [[ $VERBOSE ]] && echo $'\n'"$appname environment:"$'\n'"$new_env" unset new_env echo einfo "Checking reverse dependencies" einfo "Packages containing binaries and libraries $HEAD_TEXT" einfo "will be emerged." } get_files() { einfo "Collecting system binaries and libraries" if [[ -rs $LIST.1_files ]]; then einfo "Found existing $LIST.1_files" else # Be safe and remove any extraneous temporary files rm -f $LIST.[1-9]_* set_trap "$LIST.1_*" if [[ $SEARCH_DIRS_MASK ]]; then findMask=($SEARCH_DIRS_MASK) findMask="${findMask[@]/#/-o -path }" findMask="( ${findMask#-o } ) -prune -o" fi find ${SEARCH_DIRS[@]} $findMask -type f \( -executable -o \ -name '*.so' -o -name '*.so.*' -o -name '*.la' \) -print 2> /dev/null | sort -u > "$LIST.1_files" || die $? "find failed to list binary files (This is a bug.)" einfo "Generated new $LIST.1_files" fi } get_ldpath() { [[ $SEARCH_BROKEN && $FULL_LD_PATH ]] || return einfo 'Collecting complete LD_LIBRARY_PATH' if [[ -rs $LIST.2_ldpath ]] ; then einfo "Found existing $LIST.2_ldpath." COMPLETE_LD_LIBRARY_PATH=$(<"$LIST.2_ldpath") else set_trap "$LIST.2_ldpath" # Ensure that the "trusted" lib directories are at the start of the path COMPLETE_LD_LIBRARY_PATH=( /lib* /usr/lib* $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf) $(sed 's:/[^/]*$::' < "$LIST.1_files" | sort -ru) ) IFS=':' COMPLETE_LD_LIBRARY_PATH="${COMPLETE_LD_LIBRARY_PATH[*]}" IFS="$oIFS" echo "$COMPLETE_LD_LIBRARY_PATH" > "$LIST.2_ldpath" einfo "Generated new $LIST.2_ldpath" fi } main_checks() { einfo "Checking dynamic linking $WORKING_TEXT" if [[ -rs $LIST.3_rebuild ]]; then einfo "Found existing $LIST.3_rebuild." else [[ $LIST ]] || die 1 "$LIST" 'is undefined! (This is a bug.)' set_trap "$LIST.3_rebuild" rm -f $LIST.3* files=($(<"$LIST.1_files")) numFiles=${#files[@]}; i=0 for FILE in ${files[@]}; do if [[ $FILE != *.la ]]; then # Note: double checking seems to be faster than single with complete path # (special add ons are rare). ldd_output=$(ldd "$FILE" 2>> "$LIST.3_ldd_errors" | sort -u) ldd_status=$? # TODO: Check this for problems with sort # HACK: if LD_LIBRARY_MASK is null or undefined grep -vF doesn't work if grep -vF "${LD_LIBRARY_MASK:=$'\a'}" <<< "$ldd_output" | grep -q "$SONAME_SEARCH"; then if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$FILE" 2>/dev/null | grep -vF "$LD_LIBRARY_MASK" | grep -q "$SONAME_SEARCH"; then # FIXME: I hate duplicating code # Only build missing direct dependencies MISSING_LIBS=$( expr='s/[[:space:]]*\([^[:space:]]*\) => not found/\1/p' sed -n "$expr" <<< "$ldd_output" ) REQUIRED_LIBS=$( expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p'; objdump -x "$FILE" | sed "$expr" | sort -u ) MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS") if [[ $MISSING_LIBS ]]; then echo "obj $FILE" >> "$LIST.3_rebuild" echo_v " broken $FILE (requires $MISSING_LIBS)" fi fi else # FIXME: I hate duplicating code # Only rebuild for direct dependencies MISSING_LIBS=$( expr="/$SONAME_SEARCH/s/^\([^[:space:]]*\).*$/\1/p" sort -u <<< "$ldd_output" | sed -n "$expr" ) REQUIRED_LIBS=$( expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p'; objdump -x "$FILE" | sed "$expr" | sort -u ) MISSING_LIBS=$(grep -F "$REQUIRED_LIBS") if [[ $MISSING_LIBS ]]; then echo "obj $FILE" >> "$LIST.3_rebuild" if [[ $SEARCH_BROKEN ]]; then echo_v " broken $FILE (requires $MISSING_LIBS)" else echo_v " found $FILE" fi fi fi fi elif [[ $SEARCH_BROKEN ]]; then # Look for broken .la files for depend in $( awk -F"[=']" '/^dependency_libs/{ gsub("^-[^[:space:]]*", "", $2); gsub("[[:space:]]-[^[:space:]]*", "", $2); print $2 }' "$FILE" ); do if [[ $depend != /* && ! -e $depend ]]; then echo "obj $FILE" >> "$LIST.3_rebuild" echo_v " broken $FILE (requires $depend)" fi done fi [[ $VERBOSE ]] && progress $((++i)) $numFiles $FILE || progress $((++i)) $numFiles done if [[ $SEARCH_BROKEN ]]; then # Look for missing version for FILE in $( awk '/no version information available/{ gsub("[()]", "", $NF); print $NF }' "$LIST.3_ldd_errors" | sort -u ); do echo "obj $FILE" >> "$LIST.3_rebuild" echo_v " broken $FILE (no version information available)" done fi [[ -rs $LIST.3_rebuild ]] || clean_exit einfo "Generated new $LIST.3_rebuild" fi } get_packages() { einfo 'Assigning files to packages' if [[ -rs $LIST.4_packages_raw ]]; then einfo "Found existing $LIST.4_packages_raw" else set_trap "$LIST.4_packages*" rm -f $LIST.4* while read obj FILE; do EXACT_PKG=$(get_file_owner $FILE) if [[ $EXACT_PKG ]]; then # Strip version information PKG="${EXACT_PKG%%-r[[:digit:]]*}" PKG="${PKG%-*}" echo "$EXACT_PKG" >> $LIST.4_packages_raw echo "$FILE -> $EXACT_PKG" >> $LIST.4_package_owners echo_v " $FILE -> $PKG" else ewarn " !!! $FILE not owned by any package is broken !!!" echo "$FILE -> (none)" >> $LIST.4_package_owners echo_v -n -e "\n $FILE -> (none)" fi done < "$LIST.3_rebuild" einfo "Generated new $LIST.4_packages_raw and $LIST.4_package_owners" fi # if we find '(none)' on every line, exit out if ! grep -qvF '(none)' "$LIST.4_package_owners"; then ewarn "Found some broken files, but none of them were associated with known packages" ewarn "Unable to proceed with automatic repairs." ewarn "The broken files are listed in $LIST.4_package_owners" if [[ $VERBOSE ]]; then ewarn "The broken files are:" while read filename junk; do ewarn " $filename" done < "$LIST.4_package_owners" fi exit 0 # FIXME: Should we exit 1 here? fi } clean_packages() { einfo 'Cleaning list of packages to rebuild' if [[ -rs $LIST.4_packages ]]; then einfo "Found existing $LIST.4_packages" else sort -u $LIST.4_packages_raw > $LIST.4_packages einfo "Generated new $LIST.4_packages" fi } assign_packages_to_ebuilds() { einfo 'Assigning packages to ebuilds' if [[ -rs $LIST.4_ebuilds ]]; then einfo "Found existing $LIST.4_ebuilds" elif [[ -rs $LIST.4_packages ]]; then set_trap "$LIST.4_ebuilds" while read EXACT_PKG; do # Get the slot PKG="${EXACT_PKG%%-r[[:digit:]]*}" PKG="${PKG%-*}" SLOT=$( "$LIST.4_ebuilds" einfo "Generated new $LIST.4_ebuilds" else einfo 'Nothing to rebuild.' die 1 '(The program should have already quit, so this is a minor bug.)' fi } get_exact_ebuilds() { einfo 'Assigning files to ebuilds' if [[ -rs $LIST.4_ebuilds ]]; then einfo "Found existing $LIST.4_ebuilds" elif [[ -rs $LIST.3_rebuild ]]; then rebuildList=" $(<"$LIST.3_rebuild") " rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ }) get_file_owner "${rebuildList[@]}" > $LIST.4_ebuilds einfo "Generated new $LIST.4_ebuilds" else einfo 'Nothing to rebuild.' die 1 '(The program should have already quit, so this is a minor bug.)' fi } get_build_order() { if [[ ! $order_packages ]]; then einfo 'Skipping package ordering' return fi einfo 'Evaluating package order' if [[ -rs $LIST.5_order ]]; then einfo "Found existing $LIST.5_order" else set_trap "$LIST.5_order" RAW_REBUILD_LIST=$(<"$LIST.4_ebuilds") if [[ $RAW_REBUILD_LIST ]]; then OLD_EMERGE_DEFAULT_OPTS="$EMERGE_DEFAULT_OPTS" export EMERGE_DEFAULT_OPTS="--nospinner --pretend --oneshot --nodeps --quiet" RAW_REBUILD_LIST="=${RAW_REBUILD_LIST//[[:space:]]/ =}" REBUILD_GREP=$(emerge $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g') && emerge --deep $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g' | grep -F "$REBUILD_GREP" > $LIST.5_order || { eerror eerror 'Warning: Failed to resolve package order.' eerror 'Will merge in arbitrary order' eerror cat <<- EOF Possible reasons: - An ebuild is no longer in the portage tree. - An ebuild is masked, use /etc/portage/packages.keyword and/or /etc/portage/package.unmask to unmask it EOF for i in {1..5}; do echo -n -e '\a.' sleep 1 done rm -f "$LIST.5_order" } export EMERGE_DEFAULT_OPTS="$OLD_EMERGE_DEFAULT_OPTS" else einfo 'Nothing to rebuild.' die 1 '(The program should have already quit, so this is a minor bug.)' fi fi [[ -rs $LIST.5_order ]] && einfo "Generated new $LIST.5_order" } get_search_env echo get_files echo get_ldpath echo main_checks echo if [[ $PACKAGE_NAMES ]]; then get_packages echo clean_packages echo assign_packages_to_ebuilds else get_exact_ebuilds fi echo get_build_order echo # Clean up no longer needed environment variables unset COMPLETE_LD_LIBRARY_PATH SEARCH_DIRS SEARCH_DIRS_MASK LD_LIBRARY_MASK \ PORTAGE_ROOT [[ -rs $LIST.5_order ]] && REBUILD_LIST=($(<"$LIST.5_order")) || REBUILD_LIST=($(<"$LIST.4_ebuilds")) trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM REBUILD_LIST="=${REBUILD_LIST[@]}" REBUILD_LIST="${REBUILD_LIST//[[:space:]]/ =}" einfo 'All prepared. Starting rebuild' echo "emerge --oneshot ${EMERGE_OPTIONS[@]} $REBUILD_LIST" if is_real_merge; then for i in {1..10}; do echo -n -e '\a.' sleep 1 done echo fi # Link file descriptor #6 with stdin so --ask will work exec 6<&0 # Run in background to correctly handle Ctrl-C { EMERGE_DEFAULT_OPTS="--oneshot ${EMERGE_OPTIONS[@]}" emerge $REBUILD_LIST <&6 echo $? > $LIST.6_status } & wait # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use. exec 0<&6 6<&- if (( $(<"$LIST.6_status") != 0 )); then ewarn ewarn "$appname failed to emerge all packages." ewarn 'you have the following choices:' einfo "- If emerge failed during the build, fix the problems and re-run $appname." einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.' einfo " (and remove $LIST.5_order to be evaluated again)" einfo '- Modify the above emerge command and run it manually.' einfo '- Compile or unmerge unsatisfied packages manually,' einfo ' remove temporary files, and try again.' einfo ' (you can edit package/ebuild list first)' einfo einfo 'To remove temporary files, please run:' einfo "rm $LIST*.?_*" exit $EMERGE_STATUS elif is_real_merge; then trap_cmd() { eerror "terminated. Please remove the temporary files manually:" eerror "rm $LIST*.?_*" exit 1 } trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM einfo 'Build finished correctly. Removing temporary files...' einfo einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries' einfo 'are fixed. If some inconsistency remains, it can be orphaned file, deep' einfo 'dependency, binary package or specially evaluated library.' [[ $KEEP_TEMP ]] || rm $LIST*.?_* else einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.' fi if grep -qF '(none)' "$LIST.4_package_owners"; then ewarn "Found some broken files that weren't associated with known packages" ewarn "The broken files are listed in $LIST.4_package_owners" if [[ $VERBOSE ]]; then ewarn "The broken files are:" while read filename junk; do [[ junk = *none* ]] && ewarn " $filename" done < "$LIST.4_package_owners" fi fi