2 # Copyright 1999-2010 Gentoo Foundation
4 # revdep-rebuild: Reverse dependency rebuilder.
5 # Original Author: Stanislav Brabec
6 # Rewrite Author: Michael A. Smith
7 # Current Maintainer: Paul Varner <fuzzyray@gentoo.org>
10 # - Use more /etc/init.d/functions.sh
11 # - Try to reduce the number of global vars
20 declare -r APP_NAME="${0##*/}" # The name of this application
21 declare -r VERSION="svn"
22 declare -r OIFS="$IFS" # Save the IFS
23 declare -r ENV_FILE=0_env.rr # Contains environment variables
24 declare -r FILES_FILE=1_files.rr # Contains a list of files to search
25 declare -r LDPATH_FILE=2_ldpath.rr # Contains the LDPATH
26 declare -r BROKEN_FILE=3_broken.rr # Contains the list of broken files
27 declare -r ERRORS_FILE=3_errors.rr # Contains the ldd error output
28 declare -r RAW_FILE=4_raw.rr # Contains the raw list of packages
29 declare -r OWNERS_FILE=4_owners.rr # Contains the file owners
30 declare -r PKGS_FILE=4_pkgs.rr # Contains the unsorted bare package names
31 declare -r EBUILDS_FILE=4_ebuilds.rr # Contains the unsorted atoms
32 # (Appropriately slotted or versioned)
33 declare -r ORDER_FILE=5_order.rr # Contains the sorted atoms
34 declare -r STATUS_FILE=6_status.rr # Contains the ldd error output
49 # "Boolean" variables: Considered "true" if it has any value at all
50 # "True" indicates we should...
51 declare FULL_LD_PATH # ...search across the COMPLETE_LD_LIBRARY_PATH
52 declare KEEP_TEMP # ...not delete tempfiles from the current run
53 declare ORDER_PKGS # ...sort the atoms in deep dependency order
54 declare PACKAGE_NAMES # ...emerge by slot, not by versionated atom
55 declare RM_OLD_TEMPFILES # ...remove tempfiles from prior runs
56 declare SEARCH_BROKEN # ...search for broken libraries and binaries
57 declare VERBOSE # ...give verbose output
59 # Globals that impact portage directly:
60 declare EMERGE_DEFAULT_OPTS # String of options portage assumes to be set
61 declare EMERGE_OPTIONS # Array of options to pass to portage
62 declare PORTAGE_NICENESS # Renice to this value
63 declare PORTAGE_ROOT # The root path for portage
65 # Customizable incremental variables:
66 # These variables can be prepended to either by setting the variable in
67 # your environment prior to execution, or by placing an entry in
70 # An entry of "-*" means to clear the variable from that point forward.
71 # Example: env SEARCH_DIRS="/usr/bin -*" revdep-rebuild will set SEARCH_DIRS
72 # to contain only /usr/bin
73 declare LD_LIBRARY_MASK # Mask of specially evaluated libraries
74 declare SEARCH_DIRS # List of dirs to search for executables and libraries
75 declare SEARCH_DIRS_MASK # List of dirs not to search
78 declare OLDPROG # Previous pass through the progress meter
79 declare EXACT_PKG # Versionated atom to emerge
80 declare HEAD_TEXT # Feedback string about the search
81 declare NOCOLOR # Set to "true" not to output term colors
82 declare OK_TEXT # Feedback about a search which found no errors
83 declare RC_NOCOLOR # Hack to insure we respect NOCOLOR
84 declare REBUILD_LIST # Array of atoms to emerge
85 declare SKIP_LIST # Array of atoms that cannot be emerged (masked?)
86 declare SONAME # Soname/soname path pattern given on commandline
87 declare SONAME_SEARCH # Value of SONAME modified to match ldd's output
88 declare WORKING_TEXT # Feedback about the search
89 declare WORKING_DIR # Working directory where cache files are kept
96 setup_search_paths_and_masks
100 # Search for broken binaries
105 # Associate broken binaries with packages to rebuild
106 if [[ $PACKAGE_NAMES ]]; then
109 assign_packages_to_ebuilds
114 # Rebuild packages owning broken binaries
122 # Refuse to delete anything before we cd to our tmpdir
123 # (See mkdir_and_cd_to_tmpdir()
125 eerror "I was instructed to rm '$@'"
126 die 1 "Refusing to delete anything before changing to temporary directory."
130 # GNU find has -executable, but if our users' finds do not have that flag
131 # we emulate it with this function. Also emulates -writable and -readable.
132 # Usage: find PATH ARGS -- use find like normal, except use -executable instead
133 # of various versions of -perm /+ blah blah and hacks
135 hash find || { die 1 'find not found!'; }
136 # We can be pretty sure find itself should be executable.
137 local testsubject="$(type -P find)"
138 if [[ $(command find "$testsubject" -executable 2> /dev/null) ]]; then
139 unset -f find # We can just use the command find
140 elif [[ $(command find "$testsubject" -perm /u+x 2> /dev/null) ]]; then
142 a=(${@//-executable/-perm \/u+x})
143 a=(${a[@]//-writable/-perm \/u+w})
144 a=(${a[@]//-readable/-perm \/r+w})
145 command find "${a[@]}"
147 elif [[ $(command find "$testsubject" -perm +u+x 2> /dev/null) ]]; then
149 a=(${@//-executable/-perm +u+x})
150 a=(${a[@]//-writable/-perm +u+w})
151 a=(${a[@]//-readable/-perm +r+w})
152 command find "${a[@]}"
156 a=(${@//-executable/-exec test -x '{}' \; -print})
157 a=(${a[@]//-writable/-exec test -w '{}' \; -print})
158 a=(${a[@]//-readable/-exec test -r '{}' \; -print})
159 command find "${a[@]}"
168 ${APP_NAME}: (${VERSION})
170 Copyright (C) 2003-2010 Gentoo Foundation, Inc.
171 This is free software; see the source for copying conditions.
173 Usage: $APP_NAME [OPTIONS] [--] [EMERGE_OPTIONS]
175 Broken reverse dependency rebuilder.
177 -C, --nocolor Turn off colored output
178 -d, --debug Print way too much information (uses bash's set -xv)
179 -e, --exact Emerge based on exact package version
180 -h, --help Print this usage
181 -i, --ignore Ignore temporary files from previous runs
182 -k, --keep-temp Do not delete temporary files on exit
183 -L, --library NAME Emerge existing packages that use the library with NAME
184 --library=NAME NAME can be a full path to the library or a basic
185 regular expression (man grep)
186 -l, --no-ld-path Do not set LD_LIBRARY_PATH
187 -o, --no-order Do not check the build order
188 (Saves time, but may cause breakage.)
189 -p, --pretend Do a trial run without actually emerging anything
190 (also passed to emerge command)
191 -P, --no-progress Turn off the progress meter
192 -q, --quiet Be less verbose (also passed to emerge command)
193 -v, --verbose Be more verbose (also passed to emerge command)
195 Calls emerge, options after -- are ignored by $APP_NAME
196 and passed directly to emerge.
198 Report bugs to <http://bugs.gentoo.org>
203 # Usage: progress i n
205 # n: total number of items to process
209 local curProg=$(( $1 * 100 / $2 ))
210 (( curProg == OLDPROG )) && return # no change, output nothing
211 OLDPROG="$curProg" # must be a global variable
212 (( $1 == $2 )) && local lb=$'\n'
213 echo -ne '\r \r'"[ $curProg% ] $lb"
216 else # STDOUT is not a tty. Disable progress meter.
222 # n: number of seconds to count
225 for ((i=1; i<$1; i++)); do
232 # Replace whitespace with linebreaks, normalize repeated '/' chars, and sort -u
233 # (If any libs have whitespace in their filenames, someone needs punishment.)
235 gawk 'BEGIN {RS="[[:space:]]"}
237 /[^[:space:]]/ {gsub(/\/\/+/, "/"); print}' | sort -u
240 # Exit and optionally output to sterr
245 # Check if eerror has been loaded.
246 # Its loaded _after_ opt parsing but not before due to RC_NOCOLOR.
247 type eerror &> /dev/null
253 echo " * ${@}" >> /dev/stderr
258 # What to do when dynamic linking is consistent
260 if [[ ! $KEEP_TEMP ]]; then
262 if [[ "$WORKING_DIR" != "/var/cache/${APP_NAME}" ]]; then
263 # Remove the working directory
264 builtin cd; rmdir "$WORKING_DIR"
268 einfo "$OK_TEXT... All done. "
272 # Get the name of the package that owns a file or list of files given as args.
273 # NOTE: depends on app-misc/realpath!
277 rpath=$(realpath "${*}" 2>/dev/null)
278 # To ensure we always have something in rpath...
279 [[ -z $rpath ]] && rpath=${*}
281 # Workaround for bug 280341
282 mlib=$(echo ${*}|sed 's:/lib/:/lib64/:')
283 [[ "${*}" == "${mlib}" ]] && mlib=$(echo ${*}|sed 's:/lib64/:/lib/:')
285 # Add a space to the end of each object name to prevent false
286 # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
287 # The same for "${rpath} ".
288 find /var/db/pkg -type f -name CONTENTS -print0 |
289 xargs -0 grep -m 1 -Fl -e "${*} " -e "${rpath} " -e "${mlib} " |
290 sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
293 # Normalize some EMERGE_OPTIONS
294 normalize_emerge_opts() {
295 # Normalize some EMERGE_OPTIONS
296 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
297 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
298 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-v/--verbose})
301 # Use the color preference from portage
303 # This should still work if NOCOLOR is set by the -C flag or in the user's
305 [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr)
306 . /etc/init.d/functions.sh
309 # Die if an argument is missing.
310 die_if_missing_arg() {
311 [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1"
314 # Die because an option is not recognized.
315 die_invalid_option() {
316 # Can't use eerror and einfo because this gets called before function.sh
319 echo "Encountered unrecognized option $1." >&2
321 echo "$APP_NAME no longer automatically passes unrecognized options to portage."
322 echo "Separate emerge-only options from revdep-rebuild options with the -- flag."
324 echo "For example, $APP_NAME -v -- --ask"
326 echo "See the man page or $APP_NAME -h for more detail."
331 # Warn about deprecated options.
332 warn_deprecated_opt() {
333 # Can't use eerror and einfo because this gets called before function.sh
336 echo "Encountered deprecated option $1." >&2
337 [[ $2 ]] && echo "Please use $2 instead." >&2
340 # Get whole-word commandline options preceded by two dashes.
343 --nocolor) export NOCOLOR="yes";;
344 --no-color) warn_deprecated_opt "$1" "--nocolor"
345 export NOCOLOR="yes";;
347 --exact) unset PACKAGE_NAMES;;
350 --ignore) RM_OLD_TEMPFILES=1;;
351 --keep-temp) KEEP_TEMP=1;;
352 --library=*) # TODO: check for invalid values
354 unset SEARCH_BROKEN;;
355 --soname=*|--soname-regexp=*) # TODO: check for invalid values
356 warn_deprecated_opt "${1%=*}" "--library"
358 unset SEARCH_BROKEN;;
359 --library) # TODO: check for invalid values
360 die_if_missing_arg $1 $2
363 unset SEARCH_BROKEN;;
364 --soname|--soname-regexp) # TODO: check for invalid values
365 warn_deprecated_opt "$1" "--library"
366 die_if_missing_arg $1 $2
369 unset SEARCH_BROKEN;;
370 --no-ld-path) unset FULL_LD_PATH;;
371 --no-order) unset ORDER_PKGS;;
372 --no-progress) progress() { :; };;
373 --pretend) EMERGE_OPTIONS+=("--pretend");;
374 --quiet) echo_v() { :; }
377 EMERGE_OPTIONS+=($1);;
379 EMERGE_OPTIONS+=("--verbose");;
380 --extra-verbose) warn_deprecated_opt "$1" "--verbose"
382 EMERGE_OPTIONS+=("--verbose");;
383 --package-names) # No longer used, since it is the
384 # default. We accept it for
385 # backwards compatibility.
386 warn_deprecated_opt "$1"
388 *) die_invalid_option $1;;
393 # Get single-letter commandline options preceded by a single dash.
395 local OPT OPTSTRING OPTARG OPTIND
396 while getopts ":CdehikL:loPpqu:vX" OPT; do
398 C) # TODO: Match syntax with the rest of gentoolkit
399 export NOCOLOR="yes";;
401 e) unset PACKAGE_NAMES;;
404 i) RM_OLD_TEMPFILES=1;;
406 L) # TODO: Check for invalid values
407 SONAME="${OPTARG#*=}"
408 unset SEARCH_BROKEN;;
409 l) unset FULL_LD_PATH;;
410 o) unset ORDER_PKGS;;
411 P) progress() { :; };;
412 p) EMERGE_OPTIONS+=("--pretend");;
416 EMERGE_OPTIONS+=("--quiet");;
418 EMERGE_OPTIONS+=("--verbose");;
419 X) # No longer used, since it is the default.
420 # We accept it for backwards compatibility.
421 warn_deprecated_opt "-X"
423 *) die_invalid_option "-$OPTARG";;
428 # Get command-line options.
432 echo_v() { ewarn "$@"; }
433 unset VERBOSE KEEP_TEMP EMERGE_OPTIONS RM_OLD_TEMPFILES
442 EMERGE_OPTIONS+=("$@")
447 [[ ${1:--} = -* ]] && break
449 if [[ ${args[0]} = --* ]]; then
450 get_longopts "${args[@]}"
452 get_shortopts "${args[@]}"
454 *) die_invalid_option "$1";;
460 normalize_emerge_opts
462 # If the user is not super, add --pretend to EMERGE_OPTIONS
463 if [[ ${EMERGE_OPTIONS[@]} != *--pretend* && $UID -ne 0 ]]; then
464 ewarn "You are not superuser. Adding --pretend to emerge options."
465 EMERGE_OPTIONS+=(--pretend)
469 # Is there a --pretend or --fetchonly flag in the EMERGE_OPTIONS array?
471 [[ ${EMERGE_OPTIONS[@]} != *--pretend* &&
472 ${EMERGE_OPTIONS[@]} != *--fetchonly* ]]
475 # Clean up temporary files and exit
478 die 1 " ...terminated. Removing incomplete $@."
483 trap "cleanup_and_die $*" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
487 # Returns 0 if the first arg is found in the remaining args, 1 otherwise
488 # (Returns 2 if given fewer than 2 arguments)
490 (( $# > 1 )) || return 2
491 local IFS=$'\a' target="$1"
493 [[ $'\a'"$*"$'\a' = *$'\a'$target$'\a'* ]]
496 # Dies when it can't change directories
498 if builtin cd -P "$@"; then
499 if [[ $1 != $PWD ]]; then
500 # Some symlink malfeasance is going on
501 die 1 "Working directory expected to be $1, but it is $PWD"
504 die 1 "Unable to change working directory to '$@'"
508 # Tries not to delete any files or directories it shouldn't
511 # Anything in the FILES array in tmpdir is fair game for removal
514 [[ $APP_NAME ]] || die 1 '$APP_NAME is not defined! (This is a bug.)'
516 */*|*-r*|*-R*) die 1 "Oops, I'm not allowed to delete that. ($@)";;
519 # Don't delete files that are not listed in the array
520 # Allow no slashes or recursive deletes at all.
522 */*|-*r*|-*R*) :;; # Not OK
525 has "$i" "${FILES[@]}" && continue
526 die 1 "Oops, I'm not allowed to delete that. ($@)"
530 # delete this setup function so it's harmless to re-run
534 # Make our temporary files directory
535 # $1 - directory name
539 die 1 'Temporary file path is unset! (This is a bug.)'
540 elif [[ -d $1 ]]; then
543 die 1 "Unable to find a satisfactory location for temporary files ($1)"
545 [[ $VERBOSE ]] && einfo "Temporary cache files are located in $PWD"
551 local uid=$(python -c 'import os; import pwd; print pwd.getpwuid(os.getuid())[0]')
552 # Find a place to put temporary files
553 if [[ "$uid" == "root" ]]; then
554 local tmp_target="/var/cache/${APP_NAME}"
556 local tmp_target="$(mktemp -d -t revdep-rebuild.XXXXXXXXXX)"
559 # From here on all work is done inside the temporary directory
560 verify_tmpdir "$tmp_target"
561 WORKING_DIR="$tmp_target"
563 if [[ $SEARCH_BROKEN ]]; then
564 SONAME_SEARCH="$SONAME"
565 HEAD_TEXT="broken by a package update"
566 OK_TEXT="Dynamic linking on your system is consistent"
567 WORKING_TEXT="consistency"
569 # first case is needed to test against /path/to/foo.so
570 if [[ $SONAME = /* ]]; then
571 # Set to "<space>$SONAME<space>"
572 SONAME_SEARCH=" $SONAME "
573 # Escape the "/" characters
574 SONAME_SEARCH="${SONAME_SEARCH//\//\\/}"
576 # Set to "<tab>$SONAME<space>"
577 SONAME_SEARCH=$'\t'"$SONAME "
579 HEAD_TEXT="using $SONAME"
580 OK_TEXT="There are no dynamic links to $SONAME"
584 # If any of our temporary files are older than 1 day, remove them all
585 if [[ ! $KEEP_TEMP ]]; then
589 done < <(find -L . -maxdepth 1 -type f -name '*.rr' -mmin +1440 -print 2>/dev/null)
592 # Compare old and new environments
593 # Don't use our previous files if environment doesn't match
595 # We do not care if these emerge options change
596 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--pretend/})
597 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--fetchonly/})
598 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--verbose/})
600 SEARCH_DIRS="$SEARCH_DIRS"
601 SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK"
602 LD_LIBRARY_MASK="$LD_LIBRARY_MASK"
603 PORTAGE_ROOT="$PORTAGE_ROOT"
604 EMERGE_OPTIONS="${EMERGE_OPTIONS[@]}"
605 ORDER_PKGS="$ORDER_PKGS"
606 FULL_LD_PATH="$FULL_LD_PATH"
609 if [[ -r "$ENV_FILE" && -s "$ENV_FILE" ]]; then
610 old_env=$(<"$ENV_FILE")
611 if [[ $old_env != $new_env ]]; then
612 ewarn 'Environment mismatch from previous run, deleting temporary files...'
616 # No env file found, silently delete any other tempfiles that may exist
620 # If we should remove old tempfiles, do so
621 if [[ $RM_OLD_TEMPFILES ]]; then
624 for file in "${FILES[@]}"; do
625 if [ -e "$file" ]; then
626 chown ${uid}:portage "$file"
632 # Save the environment in a file for next time
633 echo "$new_env" > "$ENV_FILE"
635 [[ $VERBOSE ]] && echo $'\n'"$APP_NAME environment:"$'\n'"$new_env"
638 einfo "Checking reverse dependencies"
639 einfo "Packages containing binaries and libraries $HEAD_TEXT"
640 einfo "will be emerged."
644 einfo "Collecting system binaries and libraries"
645 if [[ -r "$FILES_FILE" && -s "$FILES_FILE" ]]; then
646 einfo "Found existing $FILES_FILE"
648 # Be safe and remove any extraneous temporary files
649 # Don't remove 0_env.rr - The first file in the array
650 rm -f "${FILES[@]:1}"
652 clean_trap "$FILES_FILE"
654 if [[ $SEARCH_DIRS_MASK ]]; then
655 findMask=($SEARCH_DIRS_MASK)
656 findMask="${findMask[@]/#/-o -path }"
657 findMask="( ${findMask#-o } ) -prune -o"
659 # TODO: Check this -- afaict SEARCH_DIRS isn't an array, so this should just be $SEARCH_DIRS?
660 find ${SEARCH_DIRS[@]} $findMask -type f \( -perm -u+x -o -perm -g+x -o -perm -o+x -o \
661 -name '*.so' -o -name '*.so.*' -o -name '*.la' \) -print 2> /dev/null |
662 sort -u > "$FILES_FILE" ||
663 die $? "find failed to list binary files (This is a bug.)"
664 einfo "Generated new $FILES_FILE"
668 local COMPLETE_LD_LIBRARY_PATH
669 [[ $SEARCH_BROKEN && $FULL_LD_PATH ]] || return
670 einfo 'Collecting complete LD_LIBRARY_PATH'
671 if [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]]; then
672 einfo "Found existing $LDPATH_FILE."
674 clean_trap "$LDPATH_FILE"
675 # Ensure that the "trusted" lib directories are at the start of the path
676 COMPLETE_LD_LIBRARY_PATH=(
679 $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf)
680 $(sed 's:/[^/]*$::' < "$FILES_FILE" | sort -ru)
683 COMPLETE_LD_LIBRARY_PATH="${COMPLETE_LD_LIBRARY_PATH[*]}"
685 echo "$COMPLETE_LD_LIBRARY_PATH" > "$LDPATH_FILE"
686 einfo "Generated new $LDPATH_FILE"
696 local COMPLETE_LD_LIBRARY_PATH
697 if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
698 [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]] ||
699 die 1 "Unable to find $LDPATH_FILE"
700 COMPLETE_LD_LIBRARY_PATH=$(<"$LDPATH_FILE")
702 einfo "Checking dynamic linking $WORKING_TEXT"
703 if [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]]; then
704 einfo "Found existing $BROKEN_FILE."
706 clean_trap "$BROKEN_FILE" "$ERRORS_FILE"
707 files=($(<"$FILES_FILE"))
708 numFiles="${#files[@]}"
709 for target_file in "${files[@]}"; do
710 if [[ $target_file != *.la ]]; then
711 # Note: double checking seems to be faster than single with complete path
712 # (special add ons are rare).
713 ldd_output=$(ldd "$target_file" 2>> "$ERRORS_FILE" | sort -u)
714 ldd_status=$? # TODO: Check this for problems with sort
715 # HACK: if LD_LIBRARY_MASK is null or undefined grep -vF doesn't work
716 if grep -vF "${LD_LIBRARY_MASK:=$'\a'}" <<< "$ldd_output" |
717 grep -q "$SONAME_SEARCH"; then
718 if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
719 if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$target_file" 2>/dev/null |
720 grep -vF "$LD_LIBRARY_MASK" | grep -q "$SONAME_SEARCH"; then
721 # FIXME: I hate duplicating code
722 # Only build missing direct dependencies
724 expr='s/[[:space:]]*\([^[:space:]]*\) => not found/\1/p'
725 sed -n "$expr" <<< "$ldd_output"
728 expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
729 objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
731 MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
732 if [[ $MISSING_LIBS ]]; then
733 echo "obj $target_file" >> "$BROKEN_FILE"
734 echo_v " broken $target_file (requires $MISSING_LIBS)"
738 # FIXME: I hate duplicating code
739 # Only rebuild for direct dependencies
741 expr="/$SONAME_SEARCH/s/^[[:space:]]*\([^[:space:]]*\).*$/\1/p"
742 sort -u <<< "$ldd_output" | sed -n "$expr"
745 expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
746 objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
748 MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
749 if [[ $MISSING_LIBS ]]; then
750 echo "obj $target_file" >> "$BROKEN_FILE"
751 if [[ $SEARCH_BROKEN ]]; then
752 echo_v " broken $target_file (requires $MISSING_LIBS)"
754 echo_v " found $target_file"
759 elif [[ $SEARCH_BROKEN ]]; then
760 # Look for broken .la files
761 la_SEARCH_DIRS="$SEARCH_DIRS"
766 gawk -F"[=']" '/^dependency_libs/{
770 if [[ $depend = /* && ! -e $depend ]]; then
771 echo "obj $target_file" >> "$BROKEN_FILE"
772 echo_v " broken $target_file (requires $depend)"
773 elif [[ $depend = -[LR]/* ]]; then
774 if ! [[ $'\n'${la_SEARCH_DIRS}$'\n' == *$'\n'${depend#-?}$'\n'* ]]; then
775 la_SEARCH_DIRS+=$'\n'"${depend#-?}"
777 elif [[ $depend = "-l"* ]]; then
778 la_lib="lib${depend#-l}"
781 for la_search_dir in $la_SEARCH_DIRS; do
782 if [[ -e ${la_search_dir}/${la_lib}.so || -e ${la_search_dir}/${la_lib}.a ]]; then
787 if [[ $la_broken = yes ]]; then
788 echo "obj $target_file" >> "$BROKEN_FILE"
789 echo_v " broken $target_file (requires $depend)"
793 unset la_SEARCH_DIRS la_search_dir la_broken la_lib
796 progress $((++i)) $numFiles $target_file ||
797 progress $((++i)) $numFiles
799 if [[ $SEARCH_BROKEN ]]; then
800 # Look for missing version
801 while read target_file; do
802 echo "obj $target_file" >> "$BROKEN_FILE"
803 echo_v " broken $target_file (no version information available)"
805 # Regexify LD_LIBRARY_MASK. Exclude it from the search.
806 LD_LIBRARY_MASK="${LD_LIBRARY_MASK//$'\n'/|}"
807 gawk -v ldmask="(${LD_LIBRARY_MASK//./\\\.})" '
808 /no version information available/ && $0 !~ ldmask {
809 gsub(/[()]/, "", $NF)
810 if (seen[$NF]++) next
815 [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]] || clean_exit
816 sort -u "$BROKEN_FILE" -o "$BROKEN_FILE"
817 einfo "Generated new $BROKEN_FILE"
825 einfo 'Assigning files to packages'
826 if [[ -r "$RAW_FILE" && -s "$RAW_FILE" ]]; then
827 einfo "Found existing $RAW_FILE"
829 clean_trap "$RAW_FILE" "$OWNERS_FILE"
830 while read obj target_file; do
831 EXACT_PKG=$(get_file_owner $target_file)
832 if [[ $EXACT_PKG ]]; then
833 # Strip version information
834 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
836 echo "$EXACT_PKG" >> "$RAW_FILE"
837 echo "$target_file -> $EXACT_PKG" >> "$OWNERS_FILE"
838 echo_v " $target_file -> $PKG"
840 ewarn " !!! $target_file not owned by any package is broken !!!"
841 echo "$target_file -> (none)" >> "$OWNERS_FILE"
842 echo_v " $target_file -> (none)"
844 done < "$BROKEN_FILE"
845 einfo "Generated new $RAW_FILE and $OWNERS_FILE"
847 # if we find '(none)' on every line, exit out
848 if ! grep -qvF '(none)' "$OWNERS_FILE"; then
849 ewarn "Found some broken files, but none of them were associated with known packages"
850 ewarn "Unable to proceed with automatic repairs."
851 ewarn "The broken files are listed in $OWNERS_FILE"
852 if [[ $VERBOSE ]]; then
853 ewarn "The broken files are:"
854 while read filename junk; do
856 done < "$OWNERS_FILE"
858 exit 0 # FIXME: Should we exit 1 here?
862 einfo 'Cleaning list of packages to rebuild'
863 if [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
864 einfo "Found existing $PKGS_FILE"
866 sort -u "$RAW_FILE" > "$PKGS_FILE"
867 einfo "Generated new $PKGS_FILE"
870 assign_packages_to_ebuilds() {
874 einfo 'Assigning packages to ebuilds'
875 if [[ -r "$EBUILDS_FILE" && -s "$EBUILDS_FILE" ]]; then
876 einfo "Found existing $EBUILDS_FILE"
877 elif [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
878 clean_trap "$EBUILDS_FILE"
879 while read EXACT_PKG; do
881 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
883 SLOT=$(</var/db/pkg/$EXACT_PKG/SLOT)
885 done < "$PKGS_FILE" > "$EBUILDS_FILE"
886 einfo "Generated new $EBUILDS_FILE"
888 einfo 'Nothing to rebuild.'
889 die 1 '(The program should have already quit, so this is a minor bug.)'
892 get_exact_ebuilds() {
893 einfo 'Assigning files to ebuilds'
894 if [[ -r $EBUILDS_FILE && -s $EBUILDS_FILE ]]; then
895 einfo "Found existing $EBUILDS_FILE"
896 elif [[ -r $BROKEN_FILE && -s $BROKEN_FILE ]]; then
897 rebuildList=" $(<"$BROKEN_FILE") "
898 rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ })
899 get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$EBUILDS_FILE"
900 einfo "Generated new $EBUILDS_FILE"
902 einfo 'Nothing to rebuild.'
903 die 1 '(The program should have already quit, so this is a minor bug.)'
906 list_skipped_packages() {
908 ewarn 'Portage could not find any version of the following packages it could build:'
909 ewarn "${SKIP_LIST[@]}"
911 ewarn '(Perhaps they are masked, blocked, or removed from portage.)'
912 ewarn 'Try to emerge them manually.'
916 local -r OLD_EMERGE_DEFAULT_OPTS="$EMERGE_DEFAULT_OPTS"
917 local RAW_REBUILD_LIST
920 if [[ ! $ORDER_PKGS ]]; then
921 einfo 'Skipping package ordering'
924 einfo 'Evaluating package order'
925 if [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]]; then
926 einfo "Found existing $ORDER_FILE"
928 clean_trap "$ORDER_FILE"
929 RAW_REBUILD_LIST=$(<"$EBUILDS_FILE")
930 if [[ $RAW_REBUILD_LIST ]]; then
931 export EMERGE_DEFAULT_OPTS="--nospinner --pretend --oneshot --quiet"
932 RAW_REBUILD_LIST=($RAW_REBUILD_LIST) # convert into array
933 # If PACKAGE_NAMES is defined we're using slots, not versions
934 if [[ $PACKAGE_NAMES ]]; then
935 # Eliminate atoms that can't be built
936 for i in "${!RAW_REBUILD_LIST[@]}"; do
937 if [[ "${RAW_REBUILD_LIST[i]}" = *[A-Za-z]* ]]; then
938 portageq best_visible "$PORTAGE_ROOT" "${RAW_REBUILD_LIST[i]}" >/dev/null && continue
939 SKIP_LIST+=("${RAW_REBUILD_LIST[i]}")
941 unset RAW_REBUILD_LIST[i]
943 # If RAW_REBUILD_LIST is empty, then we have nothing to build.
944 if (( ${#RAW_REBUILD_LIST[@]} == 0 )); then
945 if (( ${#SKIP_LIST[@]} == 0 )); then
946 ewarn "The list of packages to skip is empty, but there are no"
947 ewarn "packages listed to rebuild either. (This is a bug.)"
949 list_skipped_packages
951 die 1 'Warning: Portage cannot rebuild any of the necessary packages.'
954 RAW_REBUILD_LIST="${RAW_REBUILD_LIST[@]}"
955 REBUILD_GREP=$(emerge --nodeps $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g')
956 if (( ${PIPESTATUS[0]} == 0 )); then
957 emerge --deep $RAW_REBUILD_LIST |
958 sed 's/\[[^]]*\]//g' |
959 grep -F "$REBUILD_GREP" > "$ORDER_FILE"
962 # Here we use the PIPESTATUS from the second emerge, the --deep one.
963 if (( ${PIPESTATUS[0]} != 0 )); then
965 eerror 'Warning: Failed to resolve package order.'
966 eerror 'Will merge in arbitrary order'
970 - An ebuild is no longer in the portage tree.
971 - An ebuild is masked, use /etc/portage/packages.keyword
972 and/or /etc/portage/package.unmask to unmask it
977 export EMERGE_DEFAULT_OPTS="$OLD_EMERGE_DEFAULT_OPTS"
979 einfo 'Nothing to rebuild.'
980 die 1 '(The program should have already quit, so this is a minor bug.)'
983 [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]] && einfo "Generated new $ORDER_FILE"
986 show_unowned_files() {
987 if grep -qF '(none)' "$OWNERS_FILE"; then
988 ewarn "Found some broken files that weren't associated with known packages"
989 ewarn "The broken files are:"
990 while read filename junk; do
991 [[ $junk = *none* ]] && ewarn " $filename"
992 done < "$OWNERS_FILE" | gawk '!s[$0]++' # (omit dupes)
996 # Get multiple portage variables at once to speedup revdep-rebuild.
998 local ORIG_SEARCH_DIRS="$SEARCH_DIRS"
999 local ORIG_SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK"
1000 local ORIG_LD_LIBRARY_MASK="$LD_LIBRARY_MASK"
1002 unset SEARCH_DIRS_MASK
1003 unset LD_LIBRARY_MASK
1005 eval $(portageq envvar -v PORTAGE_ROOT PORTAGE_NICENESS EMERGE_DEFAULT_OPTS NOCOLOR SEARCH_DIRS SEARCH_DIRS_MASK LD_LIBRARY_MASK)
1008 SEARCH_DIRS="$ORIG_SEARCH_DIRS $SEARCH_DIRS"
1009 SEARCH_DIRS_MASK="$ORIG_SEARCH_DIRS_MASK $SEARCH_DIRS_MASK"
1010 LD_LIBRARY_MASK="$ORIG_LD_LIBRARY_MASK $LD_LIBRARY_MASK"
1014 # Setup portage and the search paths
1016 # Obey PORTAGE_NICENESS (which is incremental to the current nice value)
1017 if [[ $PORTAGE_NICENESS ]]; then
1018 current_niceness=$(nice)
1019 let PORTAGE_NICENESS=${current_niceness}+${PORTAGE_NICENESS}
1020 renice $PORTAGE_NICENESS $$ > /dev/null
1021 # Since we have already set our nice value for our processes,
1022 # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
1023 export PORTAGE_NICENESS="0"
1026 PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
1030 # Setup the paths to search (and filter the ones to avoid)
1031 setup_search_paths_and_masks() {
1032 local configfile sdir mdir skip_me filter_SEARCH_DIRS
1034 einfo "Configuring search environment for $APP_NAME"
1036 # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
1037 # portage, and the environment
1039 # Read the incremental variables from environment and portage
1040 # Until such time as portage supports these variables as incrementals
1041 # The value will be what is in /etc/make.conf
1042 # SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
1043 # SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
1044 # LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
1047 if [[ -d /etc/revdep-rebuild ]]; then
1048 for configfile in /etc/revdep-rebuild/*; do
1049 SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS)
1050 SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK)
1051 LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK)
1054 SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
1055 SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
1056 LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
1059 # Get the ROOTPATH and PATH from /etc/profile.env
1060 if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
1061 SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
1064 # Get the directories from /etc/ld.so.conf
1065 if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
1066 SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
1069 # Set the final variables
1070 SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
1071 SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
1072 LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
1073 # Filter masked paths from SEARCH_DIRS
1074 for sdir in ${SEARCH_DIRS} ; do
1076 for mdir in ${SEARCH_DIRS_MASK}; do
1077 [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break
1079 [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
1081 SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}")
1082 [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
1085 # Rebuild packages owning broken binaries
1087 if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
1088 REBUILD_LIST=( $(<"$LIST.5_order") )
1089 REBUILD_LIST="${REBUILD_LIST[@]/#/=}"
1091 REBUILD_LIST=$(sort -u "$EBUILDS_FILE")
1094 trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1096 einfo 'All prepared. Starting rebuild'
1097 echo "emerge --oneshot ${EMERGE_OPTIONS[@]} ${EMERGE_DEFAULT_OPTS} $REBUILD_LIST"
1099 is_real_merge && countdown 10
1101 # Link file descriptor #6 with stdin so --ask will work
1104 # Run in background to correctly handle Ctrl-C
1106 emerge --oneshot ${EMERGE_OPTIONS[@]} ${EMERGE_DEFAULT_OPTS} $REBUILD_LIST <&6
1107 echo $? > "$STATUS_FILE"
1111 # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
1117 if (( $(<"$STATUS_FILE") != 0 )); then
1119 ewarn "$APP_NAME failed to emerge all packages."
1120 ewarn 'you have the following choices:'
1121 einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
1122 einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
1123 einfo " (and remove $ORDER_FILE to be evaluated again)"
1124 einfo '- Modify the above emerge command and run it manually.'
1125 einfo '- Compile or unmerge unsatisfied packages manually,'
1126 einfo ' remove temporary files, and try again.'
1127 einfo ' (you can edit package/ebuild list first)'
1129 einfo 'To remove temporary files, please run:'
1130 einfo "rm ${WORKING_DIR}/*.rr"
1133 elif is_real_merge; then
1135 eerror "terminated. Please remove the temporary files manually:"
1136 eerror "rm ${WORKING_DIR}/*.rr"
1139 [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages
1140 trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1141 einfo 'Build finished correctly. Removing temporary files...'
1143 einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
1144 einfo 'are fixed. Possible reasons for remaining inconsistencies include:'
1145 einfo ' orphaned files'
1146 einfo ' deep dependencies'
1147 einfo " packages installed outside of portage's control"
1148 einfo ' specially-evaluated libraries'
1149 if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then
1152 [[ $KEEP_TEMP ]] || rm "${FILES[@]}"
1154 einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'