#! /bin/bash # Copyright 1999-2004 Gentoo Foundation # $Header: $ # revdep-rebuild: Reverse dependency rebuilder. # Original Author: Stanislav Brabec # Maintainer: Paul Varner # requires: equery # Known problems: # # In exact ebuild mode revdep-rebuild can fail to properly order packages, # which are not up to date. # http://bugs.gentoo.org/show_bug.cgi?id=23018 # # Rebuilding using --package-names mode should be default, but emerge has no # feature to update to latest version of defined SLOT. # http://bugs.gentoo.org/show_bug.cgi?id=4698 # Customizable variables: # # LD_LIBRARY_MASK - Mask of specially evaluated libraries # SEARCH_DIRS - List of directories to search for executibles 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 # Set the defaults PRELIMINARY_LD_LIBRARY_MASK="$LD_LIBRARY_MASK libodbcinst.so libodbc.so libjava.so libjvm.so" PRELIMINARY_SEARCH_DIRS="$SEARCH_DIRS /bin /sbin /usr/bin /usr/sbin /lib /usr/lib" PRELIMINARY_SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK /opt/OpenOffice.org" # Update the SEARCH_DIRS variable using /etc/profile.env, /etc/ld.so.conf, # /etc/make.conf, and the environment # Read the SEARCH_DIRS variable from /etc/make.conf if [ -e "/etc/make.conf" ] then PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS $(unset SEARCH_DIRS; . /etc/make.conf; echo $SEARCH_DIRS)" PRELIMINARY_SEARCH_DIRS_MASK="$PRELIMINARY_SEARCH_DIRS_MASK $(unset SEARCH_DIRS_MASK; . /etc/make.conf; echo $SEARCH_DIRS_MASK)" PRELIMINARY_LD_LIBRARY_MASK="$PRELIMINARY_LD_LIBRARY_MASK $(unset LD_LIBRARY_MASK; . /etc/make.conf; echo $LD_LIBRARY_MASK)" fi # Get the ROOTPATH and PATH from /etc/profile.env if [ -e "/etc/profile.env" ] then PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS $((. /etc/profile.env; echo ${ROOTPATH}:${PATH}) | tr ':' ' ')" fi # Get the directories from /etc/ld.so.conf if [ -e /etc/ld.so.conf ] then PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS $(grep -v "^#" /etc/ld.so.conf | tr '\n' ' ')" fi # Set the final variables # Note: Using $(echo $variable) removes extraneous spaces from variable assignment unset SEARCH_DIRS for i in $(echo $PRELIMINARY_SEARCH_DIRS) do [ "$i" = "-*" ] && break SEARCH_DIRS="$(echo $SEARCH_DIRS $i)" done unset SEARCH_DIRS_MASK for i in $(echo $PRELIMINARY_SEARCH_DIRS_MASK) do [ "$i" = "-*" ] && break SEARCH_DIRS_MASK="$(echo $SEARCH_DIRS_MASK $i)" done unset LD_LIBRARY_MASK for i in $(echo $PRELIMINARY_LD_LIBRARY_MASK) do [ "$i" = "-*" ] && break LD_LIBRARY_MASK="$(echo $LD_LIBRARY_MASK $i)" done # Use the color preference from portage NOCOLOR=$(portageq envvar NOCOLOR) # Base of temporary files names. LIST=~/.revdep-rebuild shopt -s nullglob shopt -s expand_aliases unalias -a # Color Definitions NO="\x1b[0m" BR="\x1b[0;01m" CY="\x1b[36;01m" GR="\x1b[32;01m" RD="\x1b[31;01m" YL="\x1b[33;01m" BL="\x1b[34;01m" alias echo_v=echo PACKAGE_NAMES=false SONAME="not found" SONAME_GREP=fgrep SEARCH_BROKEN=true while : ; do case "$1" in -h | --help ) echo "Usage: $0 [OPTIONS] [--] [EMERGE_OPTIONS]" echo echo "Broken reverse dependency rebuilder." echo echo " -X, --package-names recompile based on package names, not exact versions" echo " --soname SONAME recompile packages using library with SONAME instead" echo " of broken library (SONAME providing library must be" echo " present in the system)" echo " --soname-regexp SONAME" echo " the same as --soname, but accepts grep-style regexp" echo " -nc, --no-color turn off colored output" echo " -i, --ignore ignore temporary files from previous runs" echo " -q, --quiet be less verbose" echo echo "Calls emerge, all other options are used for it (e. g. -p, --pretend)." echo echo "Report bugs to " exit 0 ;; -X | --package-names ) PACKAGE_NAMES=true shift ;; -q | --quiet ) alias echo_v=: shift ;; --soname=* ) SONAME="${1#*=}" SEARCH_BROKEN=false shift ;; --soname ) SONAME="$2" SEARCH_BROKEN=false shift 2 ;; --soname-regexp=* ) SONAME="${1#*=}" SONAME_GREP=grep SEARCH_BROKEN=false shift ;; --soname-regexp ) SONAME="$2" SONAME_GREP=grep SEARCH_BROKEN=false shift 2 ;; -nc | --no-color ) NOCOLOR=true shift ;; -i | --ignore ) rm -f ${LIST}* shift ;; -- ) shift break ;; * ) break ;; esac done if [ "$NOCOLOR" = "yes" -o "$NOCOLOR" = "true" ] then NOCOLOR=true else NOCOLOR=false fi # Make the NOCOLOR variable visible to emerge export NOCOLOR if $NOCOLOR then NO="" BR="" CY="" GR="" RD="" YL="" BL="" fi function set_trap () { trap "rm_temp $1" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM } function rm_temp () { echo " terminated." echo "Removing incomplete $1." rm $1 echo exit 1 } if $SEARCH_BROKEN ; then SONAME_SEARCH="$SONAME" LLIST=$LIST HEAD_TEXT="broken by any package update" OK_TEXT="Dynamic linking on your system is consistent" WORKING_TEXT=" consistency" else SONAME_SEARCH=" $SONAME " LLIST=${LIST}_$(echo "$SONAME_SEARCH$SONAME" | md5sum | head -c 8) HEAD_TEXT="using $SONAME" OK_TEXT="There are no dynamic links to $SONAME" WORKING_TEXT="" fi # If our temporary files are older than 1 day, don't use them TOO_OLD=$((`date +%s` - 86400)) for file in ${LIST}* do if [ -f $file ] then FILE_AGE="$(stat -c %Y $file)" if [ $FILE_AGE -lt $TOO_OLD ] then rm -f ${LIST}* break fi fi done echo echo "Checking reverse dependencies..." echo "Packages containing binaries and libraries $HEAD_TEXT" echo "will be recompiled." echo echo -n -e "${GR}Collecting system binaries and libraries...${NO}" if [ -f $LIST.1_files ] then echo " using existing $LIST.1_files." else # Be safe and remove any extraneous temporary files rm -f ${LIST}* set_trap "$LIST.0_files" find $SEARCH_DIRS -type f \( -perm +u+x -o -name '*.so' -o -name '*.so.*' \) 2>/dev/null | sort | uniq >$LIST.0_files # Remove files that match SEARCH_DIR_MASK set_trap "$LIST.1_files" for dir in $SEARCH_DIRS_MASK do grep -v "^$dir" $LIST.0_files > $LIST.1_files cp $LIST.1_files $LIST.0_files done # Remove files that are not dynamically linked for file in $(<$LIST.0_files); do file $file | (grep -E -q "(dynamically linked|shared object)"; [ "$?" -eq 0 ] && echo $file); done > $LIST.1_files rm -f $LIST.0_files echo -e " done.\n ($LIST.1_files)" fi if $SEARCH_BROKEN ; then echo echo -n -e "${GR}Collecting complete LD_LIBRARY_PATH...${NO}" if [ -f $LIST.2_ldpath ] ; then echo " using existing $LIST.2_ldpath." else set_trap "$LIST.2_ldpath" ( sed 's:/[^/]*$::' <$LIST.1_files sed '/^#/d;s/#.*$//' $LIST.2_ldpath echo -e " done.\n ($LIST.2_ldpath)" fi export COMPLETE_LD_LIBRARY_PATH="$(cat $LIST.2_ldpath)" fi echo echo -n -e "${GR}Checking dynamic linking$WORKING_TEXT...${NO}" if [ -f $LLIST.3_rebuild ] ; then echo " using existing $LLIST.3_rebuild." else echo_v set_trap "$LLIST.3_rebuild" LD_MASK="\\( $(echo "$LD_LIBRARY_MASK" | sed 's/\./\\./g;s/ / \\| /g') \\)" echo -n >$LLIST.3_rebuild cat $LIST.1_files | while read FILE ; do # Note: double checking seems to be faster than single # with complete path (special add ons are rare). if ldd "$FILE" 2>/dev/null | grep -v "$LD_MASK" | $SONAME_GREP -q "$SONAME_SEARCH" ; then if $SEARCH_BROKEN ; then if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$FILE" 2>/dev/null | grep -v "$LD_MASK" | $SONAME_GREP -q "$SONAME_SEARCH" ; then echo "$FILE" >>$LLIST.3_rebuild echo_v " broken $FILE (requires $(ldd "$FILE" | sed -n 's/ \(.*\) => not found$/\1/p' | tr '\n' ' ' | sed 's/ $//' ))" fi else echo "$FILE" >>$LLIST.3_rebuild echo_v " found $FILE" fi fi done echo -e " done.\n ($LLIST.3_rebuild)" fi if $PACKAGE_NAMES ; then EXACT_EBUILDS=false echo echo -n -e "${GR}Assigning files to packages...${NO}" if [ -f $LLIST.4_packages_raw ] ; then echo " using existing $LLIST.4_packages_raw." else set_trap "$LLIST.4_packages_raw" echo -n >$LLIST.4_packages_raw echo -n >$LLIST.4_package_owners cat $LLIST.3_rebuild | while read FILE ; do # Ugly sed hack to strip version information PKG="$(equery -q -C belongs "^${FILE}$" | sed 's/ (.*)$//;s/-r[0-9].*$//;s/\(^.*\/*\)-.*$/\1/')" if [ -z "$PKG" ] ; then echo -n -e "\n ${RD}*** $FILE not owned by any package is broken! ***${NO}" echo "$FILE -> (none)" >> $LLIST.4_package_owners echo_v -n -e "\n $FILE -> (none)" else echo "$PKG" >> $LLIST.4_packages_raw echo "$FILE -> $PKG" >> $LLIST.4_package_owners echo_v -n -e "\n $FILE -> $PKG" fi done echo_v echo -e " done.\n ($LLIST.4_packages_raw, $LLIST.4_package_owners)" fi echo echo -n -e "${GR}Cleaning list of packages to rebuild...${NO}" if [ -f $LLIST.5_packages ] ; then echo " using existing $LLIST.5_packages." else set_trap "$LLIST.5_packages" sort -u $LLIST.4_packages_raw >$LLIST.5_packages echo -e " done.\n ($LLIST.5_packages)" fi RAW_REBUILD_LIST="$(cat $LLIST.5_packages | tr '\n' ' ')" else EXACT_EBUILDS=true echo echo -n -e "${GR}Assigning files to ebuilds...${NO}" if [ -f $LLIST.4_ebuilds ] ; then echo " using existing $LLIST.4_ebuilds." else if [ -s "$LLIST.3_rebuild" ] ; then set_trap "$LLIST.4_ebuilds" cat $LLIST.3_rebuild | sed 's/^/obj /;s/$/ /' | ( cd /var/db/pkg fgrep -l -f - */*/CONTENTS ) | sed s:/CONTENTS:: > $LLIST.4_ebuilds echo -e " done.\n ($LLIST.4_ebuilds)" else echo " Nothing to rebuild" echo -n > $LLIST.4_ebuilds fi fi RAW_REBUILD_LIST="$(cat $LLIST.4_ebuilds | sed s/^/=/ | tr '\n' ' ')" fi echo echo -n -e "${GR}Evaluating package order...${NO}" if [ -f $LLIST.5_order ] ; then echo " using existing $LLIST.5_order." else if [ ! -z "$RAW_REBUILD_LIST" ] ; then REBUILD_GREP="^\\($( (emerge --nospinner --pretend --oneshot --nodeps $RAW_REBUILD_LIST ; echo $? >$LLIST.5_status ) | sed -n 's/\./\\&/g;s/ //g;s/$/\\/;s/\[[^]]*\]//gp' | tr '\n' '|' | sed 's/|$//'))\$" if [ $(cat $LLIST.5_status) -gt 0 ] ; then echo "" echo -e "${RD}Warning: Failed to resolve package order." echo -e "Will merge in \"random\" order!${NO}" echo "Possible reasons:" echo "- An ebuild is no longer in the portage tree." echo "- An ebuild is masked, use /etc/portage/packages.keyword" echo " and/or /etc/portage/package.unmask to unmask it" for i in . . . . . ; do echo -n -e '\a.' sleep 1 done ln -f $LLIST.4_ebuilds $LLIST.5_order else emerge --nospinner --pretend --oneshot --emptytree $RAW_REBUILD_LIST | sed -n 's/ //g;s/^.*\]//p' | grep "$REBUILD_GREP" >$LLIST.5_order fi else echo -n "" >$LLIST.5_order fi echo -e " done.\n ($LLIST.5_order)" fi REBUILD_LIST="$(cat $LLIST.5_order | sed s/^/=/ | tr '\n' ' ')" trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM if [ -z "$REBUILD_LIST" ] ; then echo -e "\n${GR}$OK_TEXT... All done.${NO} " rm $LIST.[1-2]_* rm $LLIST.[3-9]_* exit 0 fi IS_REAL_MERGE=true echo " $* " | grep -q '\( -p \| --pretend \| -f \| --fetchonly \)' && IS_REAL_MERGE=false echo echo -e "${GR}All prepared. Starting rebuild...${NO}" echo "emerge --oneshot --nodeps $@ $REBUILD_LIST" if $IS_REAL_MERGE ; then for i in . . . . . . . . . . ; do echo -n -e '\a.' sleep 1 done echo fi #if $EXACT_EBUILDS ; then # Uncomment following, if you want to recompile masked ebuilds. ## FIXME: Check for PORTDIR_OVERLAY # echo -e "${GR}Temporarilly disabling package mask...${NO}" # trap "mv -i /usr/portage/profiles/package.mask.hidden /usr/portage/profiles/package.mask ; echo -e "\\n\\nTerminated." ; exit 1" \ # SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM # mv -i /usr/portage/profiles/package.mask /usr/portage/profiles/package.mask.hidden #fi # Run in background to correctly handle Ctrl-C ( emerge --oneshot --nodeps $@ $REBUILD_LIST echo $? >$LLIST.6_status ) & wait #if $EXACT_EBUILDS ; then # mv -i /usr/portage/profiles/package.mask.hidden /usr/portage/profiles/package.mask # trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM #fi if [ "$(cat $LLIST.6_status)" -gt 0 ] ; then echo echo -e "${RD}Result is not OK, you have following choices:${NO}" echo "- if emerge failed during build, fix the problems and re-run revdep-rebuild" echo " or" echo "- use -X or --package-names as first argument (try to rebuild package, not exact" echo " ebuild - ignores SLOT!)" echo " or" echo "- set ACCEPT_KEYWORDS=\"~\" and/or /etc/portage/package.unmask" echo " (and remove $LLIST.5_order to be evaluated again)" echo " or" echo "- modify the above emerge command and run it manually" echo " or" echo "- compile or unmerge unsatisfied packages manually, remove temporary files and" echo " try again (you can edit package/ebuild list first)" echo echo -e "${GR}To remove temporary files, please run:${NO}" echo "rm $LIST*.?_*" exit $(cat $LLIST.6_status) else if $IS_REAL_MERGE ; then trap "echo -e \" terminated. Please remove them manually:\nrm $LIST*.?_*\" ; exit 1" \ SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM echo -n -e "${GR}Build finished correctly. Removing temporary files...${NO} " for i in . . . . . . . . . . ; do echo -n -e '.' sleep 1 done echo rm $LIST.[1-2]_* rm $LLIST.[3-9]_* echo "You can re-run revdep-rebuild to verify that all libraries and binaries" echo "are fixed. If some inconsistency remains, it can be orphaned file, deep" echo "dependency, binary package or specially evaluated library." else echo -e "${GR}Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.${NO}" fi fi exit 0