2 # Copyright 1999-2008 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-2009 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
248 # What to do when dynamic linking is consistent
250 if [[ ! $KEEP_TEMP ]]; then
252 if [[ "$WORKING_DIR" != "/var/cache/${APP_NAME}" ]]; then
253 # Remove the working directory
254 builtin cd; rmdir "$WORKING_DIR"
258 einfo "$OK_TEXT... All done. "
262 # Get the name of the package that owns a file or list of files given as args.
263 # NOTE: depends on app-misc/realpath!
267 rpath=$(realpath "${*}" 2>/dev/null)
268 # To ensure we always have something in rpath...
269 [[ -z $rpath ]] && rpath=${*}
271 # Workaround for bug 280341
272 mlib=$(echo ${*}|sed 's:/lib/:/lib64/:')
273 [[ "${*}" == "${mlib}" ]] && mlib=$(echo ${*}|sed 's:/lib64/:/lib/:')
275 # Add a space to the end of each object name to prevent false
276 # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
277 # The same for "${rpath} ".
278 find /var/db/pkg -type f -name CONTENTS -print0 |
279 xargs -0 grep -m 1 -Fl -e "${*} " -e "${rpath} " -e "${mlib} " |
280 sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
283 # Normalize some EMERGE_OPTIONS
284 normalize_emerge_opts() {
285 # Normalize some EMERGE_OPTIONS
286 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
287 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
288 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-v/--verbose})
291 # Use the color preference from portage
293 # This should still work if NOCOLOR is set by the -C flag or in the user's
295 [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr)
296 . /etc/init.d/functions.sh
299 # Die if an argument is missing.
300 die_if_missing_arg() {
301 [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1"
304 # Die because an option is not recognized.
305 die_invalid_option() {
306 # Can't use eerror and einfo because this gets called before function.sh
309 echo "Encountered unrecognized option $1." >&2
311 echo "$APP_NAME no longer automatically passes unrecognized options to portage."
312 echo "Separate emerge-only options from revdep-rebuild options with the -- flag."
314 echo "For example, $APP_NAME -v -- --ask"
316 echo "See the man page or $APP_NAME -h for more detail."
321 # Warn about deprecated options.
322 warn_deprecated_opt() {
323 # Can't use eerror and einfo because this gets called before function.sh
326 echo "Encountered deprecated option $1." >&2
327 [[ $2 ]] && echo "Please use $2 instead." >&2
330 # Get whole-word commandline options preceded by two dashes.
333 --nocolor) export NOCOLOR="yes";;
334 --no-color) warn_deprecated_opt "$1" "--nocolor"
335 export NOCOLOR="yes";;
337 --exact) unset PACKAGE_NAMES;;
340 --ignore) RM_OLD_TEMPFILES=1;;
341 --keep-temp) KEEP_TEMP=1;;
342 --library=*) # TODO: check for invalid values
344 unset SEARCH_BROKEN;;
345 --soname=*|--soname-regexp=*) # TODO: check for invalid values
346 warn_deprecated_opt "${1%=*}" "--library"
348 unset SEARCH_BROKEN;;
349 --library) # TODO: check for invalid values
350 die_if_missing_arg $1 $2
353 unset SEARCH_BROKEN;;
354 --soname|--soname-regexp) # TODO: check for invalid values
355 warn_deprecated_opt "$1" "--library"
356 die_if_missing_arg $1 $2
359 unset SEARCH_BROKEN;;
360 --no-ld-path) unset FULL_LD_PATH;;
361 --no-order) unset ORDER_PKGS;;
362 --no-progress) progress() { :; };;
363 --pretend) EMERGE_OPTIONS+=("--pretend");;
364 --quiet) echo_v() { :; }
367 EMERGE_OPTIONS+=($1);;
369 EMERGE_OPTIONS+=("--verbose");;
370 --extra-verbose) warn_deprecated_opt "$1" "--verbose"
372 EMERGE_OPTIONS+=("--verbose");;
373 --package-names) # No longer used, since it is the
374 # default. We accept it for
375 # backwards compatibility.
376 warn_deprecated_opt "$1"
378 *) die_invalid_option $1;;
383 # Get single-letter commandline options preceded by a single dash.
385 local OPT OPTSTRING OPTARG OPTIND
386 while getopts ":CdehikL:loPpqu:vX" OPT; do
388 C) # TODO: Match syntax with the rest of gentoolkit
389 export NOCOLOR="yes";;
391 e) unset PACKAGE_NAMES;;
394 i) RM_OLD_TEMPFILES=1;;
396 L) # TODO: Check for invalid values
397 SONAME="${OPTARG#*=}"
398 unset SEARCH_BROKEN;;
399 l) unset FULL_LD_PATH;;
400 o) unset ORDER_PKGS;;
401 P) progress() { :; };;
402 p) EMERGE_OPTIONS+=("--pretend");;
406 EMERGE_OPTIONS+=("--quiet");;
408 EMERGE_OPTIONS+=("--verbose");;
409 X) # No longer used, since it is the default.
410 # We accept it for backwards compatibility.
411 warn_deprecated_opt "-X"
413 *) die_invalid_option "-$OPTARG";;
418 # Get command-line options.
422 echo_v() { ewarn "$@"; }
423 unset VERBOSE KEEP_TEMP EMERGE_OPTIONS RM_OLD_TEMPFILES
432 EMERGE_OPTIONS+=("$@")
437 [[ ${1:--} = -* ]] && break
439 if [[ ${args[0]} = --* ]]; then
440 get_longopts "${args[@]}"
442 get_shortopts "${args[@]}"
444 *) die_invalid_option "$1";;
450 normalize_emerge_opts
452 # If the user is not super, add --pretend to EMERGE_OPTIONS
453 if [[ ${EMERGE_OPTIONS[@]} != *--pretend* && $UID -ne 0 ]]; then
454 ewarn "You are not superuser. Adding --pretend to emerge options."
455 EMERGE_OPTIONS+=(--pretend)
459 # Is there a --pretend or --fetchonly flag in the EMERGE_OPTIONS array?
461 [[ ${EMERGE_OPTIONS[@]} != *--pretend* &&
462 ${EMERGE_OPTIONS[@]} != *--fetchonly* ]]
465 # Clean up temporary files and exit
468 die 1 " ...terminated. Removing incomplete $@."
473 trap "cleanup_and_die $*" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
477 # Returns 0 if the first arg is found in the remaining args, 1 otherwise
478 # (Returns 2 if given fewer than 2 arguments)
480 (( $# > 1 )) || return 2
481 local IFS=$'\a' target="$1"
483 [[ $'\a'"$*"$'\a' = *$'\a'$target$'\a'* ]]
486 # Dies when it can't change directories
488 if builtin cd -P "$@"; then
489 if [[ $1 != $PWD ]]; then
490 # Some symlink malfeasance is going on
491 die 1 "Working directory expected to be $1, but it is $PWD"
494 die 1 "Unable to change working directory to '$@'"
498 # Tries not to delete any files or directories it shouldn't
501 # Anything in the FILES array in tmpdir is fair game for removal
504 [[ $APP_NAME ]] || die 1 '$APP_NAME is not defined! (This is a bug.)'
506 */*|*-r*|*-R*) die 1 "Oops, I'm not allowed to delete that. ($@)";;
509 # Don't delete files that are not listed in the array
510 # Allow no slashes or recursive deletes at all.
512 */*|-*r*|-*R*) :;; # Not OK
515 has "$i" "${FILES[@]}" && continue
516 die 1 "Oops, I'm not allowed to delete that. ($@)"
520 # delete this setup function so it's harmless to re-run
524 # Make our temporary files directory
525 # $1 - directory name
529 die 1 'Temporary file path is unset! (This is a bug.)'
530 elif [[ -d $1 ]]; then
533 die 1 "Unable to find a satisfactory location for temporary files ($1)"
535 [[ $VERBOSE ]] && einfo "Temporary cache files are located in $PWD"
541 local uid=$(python -c 'import os; import pwd; print pwd.getpwuid(os.getuid())[0]')
542 # Find a place to put temporary files
543 if [[ "$uid" == "root" ]]; then
544 local tmp_target="/var/cache/${APP_NAME}"
546 local tmp_target="$(mktemp -d -t revdep-rebuild.XXXXXXXXXX)"
549 # From here on all work is done inside the temporary directory
550 verify_tmpdir "$tmp_target"
551 WORKING_DIR="$tmp_target"
553 if [[ $SEARCH_BROKEN ]]; then
554 SONAME_SEARCH="$SONAME"
555 HEAD_TEXT="broken by a package update"
556 OK_TEXT="Dynamic linking on your system is consistent"
557 WORKING_TEXT="consistency"
559 # first case is needed to test against /path/to/foo.so
560 if [[ $SONAME = /* ]]; then
561 # Set to "<space>$SONAME<space>"
562 SONAME_SEARCH=" $SONAME "
563 # Escape the "/" characters
564 SONAME_SEARCH="${SONAME_SEARCH//\//\\/}"
566 # Set to "<tab>$SONAME<space>"
567 SONAME_SEARCH=$'\t'"$SONAME "
569 HEAD_TEXT="using $SONAME"
570 OK_TEXT="There are no dynamic links to $SONAME"
574 # If any of our temporary files are older than 1 day, remove them all
575 if [[ ! $KEEP_TEMP ]]; then
579 done < <(find -L . -maxdepth 1 -type f -name '*.rr' -mmin +1440 -print 2>/dev/null)
582 # Compare old and new environments
583 # Don't use our previous files if environment doesn't match
585 # We do not care if these emerge options change
586 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--pretend/})
587 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--fetchonly/})
588 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--verbose/})
590 SEARCH_DIRS="$SEARCH_DIRS"
591 SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK"
592 LD_LIBRARY_MASK="$LD_LIBRARY_MASK"
593 PORTAGE_ROOT="$PORTAGE_ROOT"
594 EMERGE_OPTIONS="${EMERGE_OPTIONS[@]}"
595 ORDER_PKGS="$ORDER_PKGS"
596 FULL_LD_PATH="$FULL_LD_PATH"
599 if [[ -r "$ENV_FILE" && -s "$ENV_FILE" ]]; then
600 old_env=$(<"$ENV_FILE")
601 if [[ $old_env != $new_env ]]; then
602 ewarn 'Environment mismatch from previous run, deleting temporary files...'
606 # No env file found, silently delete any other tempfiles that may exist
610 # If we should remove old tempfiles, do so
611 if [[ $RM_OLD_TEMPFILES ]]; then
614 for file in "${FILES[@]}"; do
615 if [ -e "$file" ]; then
616 chown ${uid}:portage "$file"
622 # Save the environment in a file for next time
623 echo "$new_env" > "$ENV_FILE"
625 [[ $VERBOSE ]] && echo $'\n'"$APP_NAME environment:"$'\n'"$new_env"
628 einfo "Checking reverse dependencies"
629 einfo "Packages containing binaries and libraries $HEAD_TEXT"
630 einfo "will be emerged."
634 einfo "Collecting system binaries and libraries"
635 if [[ -r "$FILES_FILE" && -s "$FILES_FILE" ]]; then
636 einfo "Found existing $FILES_FILE"
638 # Be safe and remove any extraneous temporary files
639 # Don't remove 0_env.rr - The first file in the array
640 rm -f "${FILES[@]:1}"
642 clean_trap "$FILES_FILE"
644 if [[ $SEARCH_DIRS_MASK ]]; then
645 findMask=($SEARCH_DIRS_MASK)
646 findMask="${findMask[@]/#/-o -path }"
647 findMask="( ${findMask#-o } ) -prune -o"
649 # TODO: Check this -- afaict SEARCH_DIRS isn't an array, so this should just be $SEARCH_DIRS?
650 find ${SEARCH_DIRS[@]} $findMask -type f \( -perm -u+x -o -perm -g+x -o -perm -o+x -o \
651 -name '*.so' -o -name '*.so.*' -o -name '*.la' \) -print 2> /dev/null |
652 sort -u > "$FILES_FILE" ||
653 die $? "find failed to list binary files (This is a bug.)"
654 einfo "Generated new $FILES_FILE"
658 local COMPLETE_LD_LIBRARY_PATH
659 [[ $SEARCH_BROKEN && $FULL_LD_PATH ]] || return
660 einfo 'Collecting complete LD_LIBRARY_PATH'
661 if [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]]; then
662 einfo "Found existing $LDPATH_FILE."
664 clean_trap "$LDPATH_FILE"
665 # Ensure that the "trusted" lib directories are at the start of the path
666 COMPLETE_LD_LIBRARY_PATH=(
669 $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf)
670 $(sed 's:/[^/]*$::' < "$FILES_FILE" | sort -ru)
673 COMPLETE_LD_LIBRARY_PATH="${COMPLETE_LD_LIBRARY_PATH[*]}"
675 echo "$COMPLETE_LD_LIBRARY_PATH" > "$LDPATH_FILE"
676 einfo "Generated new $LDPATH_FILE"
686 local COMPLETE_LD_LIBRARY_PATH
687 if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
688 [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]] ||
689 die 1 "Unable to find $LDPATH_FILE"
690 COMPLETE_LD_LIBRARY_PATH=$(<"$LDPATH_FILE")
692 einfo "Checking dynamic linking $WORKING_TEXT"
693 if [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]]; then
694 einfo "Found existing $BROKEN_FILE."
696 clean_trap "$BROKEN_FILE" "$ERRORS_FILE"
697 files=($(<"$FILES_FILE"))
698 numFiles="${#files[@]}"
699 for target_file in "${files[@]}"; do
700 if [[ $target_file != *.la ]]; then
701 # Note: double checking seems to be faster than single with complete path
702 # (special add ons are rare).
703 ldd_output=$(ldd "$target_file" 2>> "$ERRORS_FILE" | sort -u)
704 ldd_status=$? # TODO: Check this for problems with sort
705 # HACK: if LD_LIBRARY_MASK is null or undefined grep -vF doesn't work
706 if grep -vF "${LD_LIBRARY_MASK:=$'\a'}" <<< "$ldd_output" |
707 grep -q "$SONAME_SEARCH"; then
708 if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
709 if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$target_file" 2>/dev/null |
710 grep -vF "$LD_LIBRARY_MASK" | grep -q "$SONAME_SEARCH"; then
711 # FIXME: I hate duplicating code
712 # Only build missing direct dependencies
714 expr='s/[[:space:]]*\([^[:space:]]*\) => not found/\1/p'
715 sed -n "$expr" <<< "$ldd_output"
718 expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
719 objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
721 MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
722 if [[ $MISSING_LIBS ]]; then
723 echo "obj $target_file" >> "$BROKEN_FILE"
724 echo_v " broken $target_file (requires $MISSING_LIBS)"
728 # FIXME: I hate duplicating code
729 # Only rebuild for direct dependencies
731 expr="/$SONAME_SEARCH/s/^[[:space:]]*\([^[:space:]]*\).*$/\1/p"
732 sort -u <<< "$ldd_output" | sed -n "$expr"
735 expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
736 objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
738 MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
739 if [[ $MISSING_LIBS ]]; then
740 echo "obj $target_file" >> "$BROKEN_FILE"
741 if [[ $SEARCH_BROKEN ]]; then
742 echo_v " broken $target_file (requires $MISSING_LIBS)"
744 echo_v " found $target_file"
749 elif [[ $SEARCH_BROKEN ]]; then
750 # Look for broken .la files
751 la_SEARCH_DIRS="$SEARCH_DIRS"
756 gawk -F"[=']" '/^dependency_libs/{
760 if [[ $depend = /* && ! -e $depend ]]; then
761 echo "obj $target_file" >> "$BROKEN_FILE"
762 echo_v " broken $target_file (requires $depend)"
763 elif [[ $depend = -[LR]/* ]]; then
764 if ! [[ $'\n'${la_SEARCH_DIRS}$'\n' == *$'\n'${depend#-?}$'\n'* ]]; then
765 la_SEARCH_DIRS+=$'\n'"${depend#-?}"
767 elif [[ $depend = "-l"* ]]; then
768 la_lib="lib${depend#-l}"
771 for la_search_dir in $la_SEARCH_DIRS; do
772 if [[ -e ${la_search_dir}/${la_lib}.so || -e ${la_search_dir}/${la_lib}.a ]]; then
777 if [[ $la_broken = yes ]]; then
778 echo "obj $target_file" >> "$BROKEN_FILE"
779 echo_v " broken $target_file (requires $depend)"
783 unset la_SEARCH_DIRS la_search_dir la_broken la_lib
786 progress $((++i)) $numFiles $target_file ||
787 progress $((++i)) $numFiles
789 if [[ $SEARCH_BROKEN ]]; then
790 # Look for missing version
791 while read target_file; do
792 echo "obj $target_file" >> "$BROKEN_FILE"
793 echo_v " broken $target_file (no version information available)"
795 # Regexify LD_LIBRARY_MASK. Exclude it from the search.
796 LD_LIBRARY_MASK="${LD_LIBRARY_MASK//$'\n'/|}"
797 gawk -v ldmask="(${LD_LIBRARY_MASK//./\\\.})" '
798 /no version information available/ && $0 !~ ldmask {
799 gsub(/[()]/, "", $NF)
800 if (seen[$NF]++) next
805 [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]] || clean_exit
806 sort -u "$BROKEN_FILE" -o "$BROKEN_FILE"
807 einfo "Generated new $BROKEN_FILE"
815 einfo 'Assigning files to packages'
816 if [[ -r "$RAW_FILE" && -s "$RAW_FILE" ]]; then
817 einfo "Found existing $RAW_FILE"
819 clean_trap "$RAW_FILE" "$OWNERS_FILE"
820 while read obj target_file; do
821 EXACT_PKG=$(get_file_owner $target_file)
822 if [[ $EXACT_PKG ]]; then
823 # Strip version information
824 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
826 echo "$EXACT_PKG" >> "$RAW_FILE"
827 echo "$target_file -> $EXACT_PKG" >> "$OWNERS_FILE"
828 echo_v " $target_file -> $PKG"
830 ewarn " !!! $target_file not owned by any package is broken !!!"
831 echo "$target_file -> (none)" >> "$OWNERS_FILE"
832 echo_v " $target_file -> (none)"
834 done < "$BROKEN_FILE"
835 einfo "Generated new $RAW_FILE and $OWNERS_FILE"
837 # if we find '(none)' on every line, exit out
838 if ! grep -qvF '(none)' "$OWNERS_FILE"; then
839 ewarn "Found some broken files, but none of them were associated with known packages"
840 ewarn "Unable to proceed with automatic repairs."
841 ewarn "The broken files are listed in $OWNERS_FILE"
842 if [[ $VERBOSE ]]; then
843 ewarn "The broken files are:"
844 while read filename junk; do
846 done < "$OWNERS_FILE"
848 exit 0 # FIXME: Should we exit 1 here?
852 einfo 'Cleaning list of packages to rebuild'
853 if [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
854 einfo "Found existing $PKGS_FILE"
856 sort -u "$RAW_FILE" > "$PKGS_FILE"
857 einfo "Generated new $PKGS_FILE"
860 assign_packages_to_ebuilds() {
864 einfo 'Assigning packages to ebuilds'
865 if [[ -r "$EBUILDS_FILE" && -s "$EBUILDS_FILE" ]]; then
866 einfo "Found existing $EBUILDS_FILE"
867 elif [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
868 clean_trap "$EBUILDS_FILE"
869 while read EXACT_PKG; do
871 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
873 SLOT=$(</var/db/pkg/$EXACT_PKG/SLOT)
875 done < "$PKGS_FILE" > "$EBUILDS_FILE"
876 einfo "Generated new $EBUILDS_FILE"
878 einfo 'Nothing to rebuild.'
879 die 1 '(The program should have already quit, so this is a minor bug.)'
882 get_exact_ebuilds() {
883 einfo 'Assigning files to ebuilds'
884 if [[ -r $EBUILDS_FILE && -s $EBUILDS_FILE ]]; then
885 einfo "Found existing $EBUILDS_FILE"
886 elif [[ -r $BROKEN_FILE && -s $BROKEN_FILE ]]; then
887 rebuildList=" $(<"$BROKEN_FILE") "
888 rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ })
889 get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$EBUILDS_FILE"
890 einfo "Generated new $EBUILDS_FILE"
892 einfo 'Nothing to rebuild.'
893 die 1 '(The program should have already quit, so this is a minor bug.)'
896 list_skipped_packages() {
898 ewarn 'Portage could not find any version of the following packages it could build:'
899 ewarn "${SKIP_LIST[@]}"
901 ewarn '(Perhaps they are masked, blocked, or removed from portage.)'
902 ewarn 'Try to emerge them manually.'
906 local -r OLD_EMERGE_DEFAULT_OPTS="$EMERGE_DEFAULT_OPTS"
907 local RAW_REBUILD_LIST
910 if [[ ! $ORDER_PKGS ]]; then
911 einfo 'Skipping package ordering'
914 einfo 'Evaluating package order'
915 if [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]]; then
916 einfo "Found existing $ORDER_FILE"
918 clean_trap "$ORDER_FILE"
919 RAW_REBUILD_LIST=$(<"$EBUILDS_FILE")
920 if [[ $RAW_REBUILD_LIST ]]; then
921 export EMERGE_DEFAULT_OPTS="--nospinner --pretend --oneshot --quiet"
922 RAW_REBUILD_LIST=($RAW_REBUILD_LIST) # convert into array
923 # If PACKAGE_NAMES is defined we're using slots, not versions
924 if [[ $PACKAGE_NAMES ]]; then
925 # Eliminate atoms that can't be built
926 for i in "${!RAW_REBUILD_LIST[@]}"; do
927 if [[ "${RAW_REBUILD_LIST[i]}" = *[A-Za-z]* ]]; then
928 portageq best_visible "$PORTAGE_ROOT" "${RAW_REBUILD_LIST[i]}" >/dev/null && continue
929 SKIP_LIST+=("${RAW_REBUILD_LIST[i]}")
931 unset RAW_REBUILD_LIST[i]
933 # If RAW_REBUILD_LIST is empty, then we have nothing to build.
934 if (( ${#RAW_REBUILD_LIST[@]} == 0 )); then
935 if (( ${#SKIP_LIST[@]} == 0 )); then
936 ewarn "The list of packages to skip is empty, but there are no"
937 ewarn "packages listed to rebuild either. (This is a bug.)"
939 list_skipped_packages
941 die 1 'Warning: Portage cannot rebuild any of the necessary packages.'
944 RAW_REBUILD_LIST="${RAW_REBUILD_LIST[@]}"
945 REBUILD_GREP=$(emerge --nodeps $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g')
946 if (( ${PIPESTATUS[0]} == 0 )); then
947 emerge --deep $RAW_REBUILD_LIST |
948 sed 's/\[[^]]*\]//g' |
949 grep -F "$REBUILD_GREP" > "$ORDER_FILE"
952 # Here we use the PIPESTATUS from the second emerge, the --deep one.
953 if (( ${PIPESTATUS[0]} != 0 )); then
955 eerror 'Warning: Failed to resolve package order.'
956 eerror 'Will merge in arbitrary order'
960 - An ebuild is no longer in the portage tree.
961 - An ebuild is masked, use /etc/portage/packages.keyword
962 and/or /etc/portage/package.unmask to unmask it
967 export EMERGE_DEFAULT_OPTS="$OLD_EMERGE_DEFAULT_OPTS"
969 einfo 'Nothing to rebuild.'
970 die 1 '(The program should have already quit, so this is a minor bug.)'
973 [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]] && einfo "Generated new $ORDER_FILE"
976 show_unowned_files() {
977 if grep -qF '(none)' "$OWNERS_FILE"; then
978 ewarn "Found some broken files that weren't associated with known packages"
979 ewarn "The broken files are:"
980 while read filename junk; do
981 [[ $junk = *none* ]] && ewarn " $filename"
982 done < "$OWNERS_FILE" | gawk '!s[$0]++' # (omit dupes)
986 # Get multiple portage variables at once to speedup revdep-rebuild.
1000 # one value per line
1002 results=( $(unset SEARCH_DIRS; unset SEARCH_DIRS_MASK; unset LD_LIBRARY_MASK; portageq envvar ${query_vars[*]}) )
1005 PORTAGE_ROOT=${results[0]}
1006 PORTAGE_NICENESS=${results[1]}
1007 EMERGE_DEFAULT_OPTS=${results[2]}
1008 export NOCOLOR=${results[3]}
1009 SEARCH_DIRS+=" "${results[4]}
1010 SEARCH_DIRS_MASK+=" "${results[5]}
1011 LD_LIBRARY_MASK+=" "${results[6]}
1015 # Setup portage and the search paths
1017 # Obey PORTAGE_NICENESS (which is incremental to the current nice value)
1018 if [[ $PORTAGE_NICENESS ]]; then
1019 current_niceness=$(nice)
1020 let PORTAGE_NICENESS=${current_niceness}+${PORTAGE_NICENESS}
1021 renice $PORTAGE_NICENESS $$ > /dev/null
1022 # Since we have already set our nice value for our processes,
1023 # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
1024 export PORTAGE_NICENESS="0"
1027 PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
1031 # Setup the paths to search (and filter the ones to avoid)
1032 setup_search_paths_and_masks() {
1033 local configfile sdir mdir skip_me filter_SEARCH_DIRS
1035 einfo "Configuring search environment for $APP_NAME"
1037 # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
1038 # portage, and the environment
1040 # Read the incremental variables from environment and portage
1041 # Until such time as portage supports these variables as incrementals
1042 # The value will be what is in /etc/make.conf
1043 # SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
1044 # SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
1045 # LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
1048 if [[ -d /etc/revdep-rebuild ]]; then
1049 for configfile in /etc/revdep-rebuild/*; do
1050 SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS)
1051 SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK)
1052 LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK)
1055 SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
1056 SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
1057 LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
1060 # Get the ROOTPATH and PATH from /etc/profile.env
1061 if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
1062 SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
1065 # Get the directories from /etc/ld.so.conf
1066 if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
1067 SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
1070 # Set the final variables
1071 SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
1072 SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
1073 LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
1074 # Filter masked paths from SEARCH_DIRS
1075 for sdir in ${SEARCH_DIRS} ; do
1077 for mdir in ${SEARCH_DIRS_MASK}; do
1078 [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break
1080 [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
1082 SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}")
1083 [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
1086 # Rebuild packages owning broken binaries
1088 if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
1089 REBUILD_LIST=( $(<"$LIST.5_order") )
1090 REBUILD_LIST="${REBUILD_LIST[@]/#/=}"
1092 REBUILD_LIST=$(sort -u "$EBUILDS_FILE")
1095 trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1097 einfo 'All prepared. Starting rebuild'
1098 echo "emerge --oneshot ${EMERGE_OPTIONS[@]} ${EMERGE_DEFAULT_OPTS} $REBUILD_LIST"
1100 is_real_merge && countdown 10
1102 # Link file descriptor #6 with stdin so --ask will work
1105 # Run in background to correctly handle Ctrl-C
1107 emerge --oneshot ${EMERGE_OPTIONS[@]} ${EMERGE_DEFAULT_OPTS} $REBUILD_LIST <&6
1108 echo $? > "$STATUS_FILE"
1112 # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
1118 if (( $(<"$STATUS_FILE") != 0 )); then
1120 ewarn "$APP_NAME failed to emerge all packages."
1121 ewarn 'you have the following choices:'
1122 einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
1123 einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
1124 einfo " (and remove $ORDER_FILE to be evaluated again)"
1125 einfo '- Modify the above emerge command and run it manually.'
1126 einfo '- Compile or unmerge unsatisfied packages manually,'
1127 einfo ' remove temporary files, and try again.'
1128 einfo ' (you can edit package/ebuild list first)'
1130 einfo 'To remove temporary files, please run:'
1131 einfo "rm ${WORKING_DIR}/*.rr"
1134 elif is_real_merge; then
1136 eerror "terminated. Please remove the temporary files manually:"
1137 eerror "rm ${WORKING_DIR}/*.rr"
1140 [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages
1141 trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1142 einfo 'Build finished correctly. Removing temporary files...'
1144 einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
1145 einfo 'are fixed. Possible reasons for remaining inconsistencies include:'
1146 einfo ' orphaned files'
1147 einfo ' deep dependencies'
1148 einfo " packages installed outside of portage's control"
1149 einfo ' specially-evaluated libraries'
1150 if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then
1153 [[ $KEEP_TEMP ]] || rm "${FILES[@]}"
1155 einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'