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 OIFS="$IFS" # Save the IFS
22 declare -r ENV_FILE=0_env.rr # Contains environment variables
23 declare -r FILES_FILE=1_files.rr # Contains a list of files to search
24 declare -r LDPATH_FILE=2_ldpath.rr # Contains the LDPATH
25 declare -r BROKEN_FILE=3_broken.rr # Contains the list of broken files
26 declare -r ERRORS_FILE=3_errors.rr # Contains the ldd error output
27 declare -r RAW_FILE=4_raw.rr # Contains the raw list of packages
28 declare -r OWNERS_FILE=4_owners.rr # Contains the file owners
29 declare -r PKGS_FILE=4_pkgs.rr # Contains the unsorted bare package names
30 declare -r EBUILDS_FILE=4_ebuilds.rr # Contains the unsorted atoms
31 # (Appropriately slotted or versioned)
32 declare -r ORDER_FILE=5_order.rr # Contains the sorted atoms
33 declare -r STATUS_FILE=6_status.rr # Contains the ldd error output
48 # "Boolean" variables: Considered "true" if it has any value at all
49 # "True" indicates we should...
50 declare FULL_LD_PATH # ...search across the COMPLETE_LD_LIBRARY_PATH
51 declare KEEP_TEMP # ...not delete tempfiles from the current run
52 declare ORDER_PKGS # ...sort the atoms in deep dependency order
53 declare PACKAGE_NAMES # ...emerge by slot, not by versionated atom
54 declare RM_OLD_TEMPFILES # ...remove tempfiles from prior runs
55 declare SEARCH_BROKEN # ...search for broken libraries and binaries
56 declare VERBOSE # ...give verbose output
58 # Globals that impact portage directly:
59 declare EMERGE_DEFAULT_OPTS # String of options portage assumes to be set
60 declare EMERGE_OPTIONS # Array of options to pass to portage
61 declare PORTAGE_NICENESS # Renice to this value
62 declare PORTAGE_ROOT # The root path for portage
64 # Customizable incremental variables:
65 # These variables can be prepended to either by setting the variable in
66 # your environment prior to execution, or by placing an entry in
69 # An entry of "-*" means to clear the variable from that point forward.
70 # Example: env SEARCH_DIRS="/usr/bin -*" revdep-rebuild will set SEARCH_DIRS
71 # to contain only /usr/bin
72 declare LD_LIBRARY_MASK # Mask of specially evaluated libraries
73 declare SEARCH_DIRS # List of dirs to search for executables and libraries
74 declare SEARCH_DIRS_MASK # List of dirs not to search
77 declare OLDPROG # Previous pass through the progress meter
78 declare EXACT_PKG # Versionated atom to emerge
79 declare HEAD_TEXT # Feedback string about the search
80 declare NOCOLOR # Set to "true" not to output term colors
81 declare OK_TEXT # Feedback about a search which found no errors
82 declare RC_NOCOLOR # Hack to insure we respect NOCOLOR
83 declare REBUILD_LIST # Array of atoms to emerge
84 declare SKIP_LIST # Array of atoms that cannot be emerged (masked?)
85 declare SONAME # Soname/soname path pattern given on commandline
86 declare SONAME_SEARCH # Value of SONAME modified to match ldd's output
87 declare WORKING_TEXT # Feedback about the search
88 declare WORKING_DIR # Working directory where cache files are kept
95 setup_search_paths_and_masks
99 # Search for broken binaries
104 # Associate broken binaries with packages to rebuild
105 if [[ $PACKAGE_NAMES ]]; then
108 assign_packages_to_ebuilds
113 # Rebuild packages owning broken binaries
121 # Refuse to delete anything before we cd to our tmpdir
122 # (See mkdir_and_cd_to_tmpdir()
124 eerror "I was instructed to rm '$@'"
125 die 1 "Refusing to delete anything before changing to temporary directory."
129 # GNU find has -executable, but if our users' finds do not have that flag
130 # we emulate it with this function. Also emulates -writable and -readable.
131 # Usage: find PATH ARGS -- use find like normal, except use -executable instead
132 # of various versions of -perm /+ blah blah and hacks
134 hash find || { die 1 'find not found!'; }
135 # We can be pretty sure find itself should be executable.
136 local testsubject="$(type -P find)"
137 if [[ $(command find "$testsubject" -executable 2> /dev/null) ]]; then
138 unset -f find # We can just use the command find
139 elif [[ $(command find "$testsubject" -perm /u+x 2> /dev/null) ]]; then
141 a=(${@//-executable/-perm \/u+x})
142 a=(${a[@]//-writable/-perm \/u+w})
143 a=(${a[@]//-readable/-perm \/r+w})
144 command find "${a[@]}"
146 elif [[ $(command find "$testsubject" -perm +u+x 2> /dev/null) ]]; then
148 a=(${@//-executable/-perm +u+x})
149 a=(${a[@]//-writable/-perm +u+w})
150 a=(${a[@]//-readable/-perm +r+w})
151 command find "${a[@]}"
155 a=(${@//-executable/-exec test -x '{}' \; -print})
156 a=(${a[@]//-writable/-exec test -w '{}' \; -print})
157 a=(${a[@]//-readable/-exec test -r '{}' \; -print})
158 command find "${a[@]}"
167 Usage: $APP_NAME [OPTIONS] [--] [EMERGE_OPTIONS]
169 Broken reverse dependency rebuilder.
171 -C, --nocolor Turn off colored output
172 -d, --debug Print way too much information (uses bash's set -xv)
173 -e, --exact Emerge based on exact package version
174 -h, --help Print this usage
175 -i, --ignore Ignore temporary files from previous runs
176 -k, --keep-temp Do not delete temporary files on exit
177 -L, --library NAME Emerge existing packages that use the library with NAME
178 --library=NAME NAME can be a full path to the library or a basic
179 regular expression (man grep)
180 -l, --no-ld-path Do not set LD_LIBRARY_PATH
181 -o, --no-order Do not check the build order
182 (Saves time, but may cause breakage.)
183 -p, --pretend Do a trial run without actually emerging anything
184 (also passed to emerge command)
185 -P, --no-progress Turn off the progress meter
186 -q, --quiet Be less verbose (also passed to emerge command)
187 -v, --verbose Be more verbose (also passed to emerge command)
189 Calls emerge, options after -- are ignored by $APP_NAME
190 and passed directly to emerge.
192 Report bugs to <http://bugs.gentoo.org>
196 # Usage: progress i n
198 # n: total number of items to process
202 local curProg=$(( $1 * 100 / $2 ))
203 (( curProg == OLDPROG )) && return # no change, output nothing
204 OLDPROG="$curProg" # must be a global variable
205 (( $1 == $2 )) && local lb=$'\n'
206 echo -ne '\r \r'"[ $curProg% ] $lb"
209 else # STDOUT is not a tty. Disable progress meter.
215 # n: number of seconds to count
218 for ((i=1; i<$1; i++)); do
225 # Replace whitespace with linebreaks, normalize repeated '/' chars, and sort -u
226 # (If any libs have whitespace in their filenames, someone needs punishment.)
228 gawk 'BEGIN {RS="[[:space:]]"}
230 /[^[:space:]]/ {gsub(/\/\/+/, "/"); print}' | sort -u
233 # Exit and optionally output to sterr
241 # What to do when dynamic linking is consistent
243 if [[ ! $KEEP_TEMP ]]; then
245 if [[ "$WORKING_DIR" != "/var/cache/${APP_NAME}" ]]; then
246 # Remove the working directory
247 builtin cd; rmdir "$WORKING_DIR"
251 einfo "$OK_TEXT... All done. "
255 # Get the name of the package that owns a file or list of files given as args.
256 # NOTE: depends on app-misc/realpath!
259 # Add a space to the end of each object name to prevent false
260 # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
261 # The same for "${rpath} ".
263 rpath=$(realpath "${*}" 2>/dev/null)
264 # To ensure we always have something in rpath...
265 [[ -z $rpath ]] && rpath=${*}
267 find -L /var/db/pkg -name CONTENTS -print0 |
268 xargs -0 grep -Fl -e "${*} " -e "${rpath} " |
269 sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
272 # Normalize some EMERGE_OPTIONS
273 normalize_emerge_opts() {
274 # Normalize some EMERGE_OPTIONS
275 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
276 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
277 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-v/--verbose})
280 # Use the color preference from portage
282 # This should still work if NOCOLOR is set by the -C flag or in the user's
284 [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr)
285 . /etc/init.d/functions.sh
288 # Die if an argument is missing.
289 die_if_missing_arg() {
290 [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1"
293 # Die because an option is not recognized.
294 die_invalid_option() {
295 # Can't use eerror and einfo because this gets called before function.sh
298 echo "Encountered unrecognized option $1." >&2
300 echo "$APP_NAME no longer automatically passes unrecognized options to portage."
301 echo "Separate emerge-only options from revdep-rebuild options with the -- flag."
303 echo "For example, $APP_NAME -v -- --ask"
305 echo "See the man page or $APP_NAME -h for more detail."
310 # Warn about deprecated options.
311 warn_deprecated_opt() {
312 # Can't use eerror and einfo because this gets called before function.sh
315 echo "Encountered deprecated option $1." >&2
316 [[ $2 ]] && echo "Please use $2 instead." >&2
319 # Get whole-word commandline options preceded by two dashes.
322 --nocolor) export NOCOLOR="yes";;
323 --no-color) warn_deprecated_opt "$1" "--nocolor"
324 export NOCOLOR="yes";;
326 --exact) unset PACKAGE_NAMES;;
329 --ignore) RM_OLD_TEMPFILES=1;;
330 --keep-temp) KEEP_TEMP=1;;
331 --library=*) # TODO: check for invalid values
333 unset SEARCH_BROKEN;;
334 --soname=*|--soname-regexp=*) # TODO: check for invalid values
335 warn_deprecated_opt "${1%=*}" "--library"
337 unset SEARCH_BROKEN;;
338 --library) # TODO: check for invalid values
339 die_if_missing_arg $1 $2
342 unset SEARCH_BROKEN;;
343 --soname|--soname-regexp) # TODO: check for invalid values
344 warn_deprecated_opt "$1" "--library"
345 die_if_missing_arg $1 $2
348 unset SEARCH_BROKEN;;
349 --no-ld-path) unset FULL_LD_PATH;;
350 --no-order) unset ORDER_PKGS;;
351 --no-progress) progress() { :; };;
352 --pretend) EMERGE_OPTIONS+=("--pretend");;
353 --quiet) echo_v() { :; }
356 EMERGE_OPTIONS+=($1);;
358 EMERGE_OPTIONS+=("--verbose");;
359 --extra-verbose) warn_deprecated_opt "$1" "--verbose"
361 EMERGE_OPTIONS+=("--verbose");;
362 --package-names) # No longer used, since it is the
363 # default. We accept it for
364 # backwards compatibility.
365 warn_deprecated_opt "$1"
367 *) die_invalid_option $1;;
372 # Get single-letter commandline options preceded by a single dash.
374 local OPT OPTSTRING OPTARG OPTIND
375 while getopts ":CdehikL:loPpqu:vX" OPT; do
377 C) # TODO: Match syntax with the rest of gentoolkit
378 export NOCOLOR="yes";;
380 e) unset PACKAGE_NAMES;;
383 i) RM_OLD_TEMPFILES=1;;
385 L) # TODO: Check for invalid values
386 SONAME="${OPTARG#*=}"
387 unset SEARCH_BROKEN;;
388 l) unset FULL_LD_PATH;;
389 o) unset ORDER_PKGS;;
390 P) progress() { :; };;
391 p) EMERGE_OPTIONS+=("--pretend");;
395 EMERGE_OPTIONS+=("--quiet");;
397 EMERGE_OPTIONS+=("--verbose");;
398 X) # No longer used, since it is the default.
399 # We accept it for backwards compatibility.
400 warn_deprecated_opt "-X"
402 *) die_invalid_option "-$OPTARG";;
407 # Get command-line options.
411 echo_v() { ewarn "$@"; }
412 unset VERBOSE KEEP_TEMP EMERGE_OPTIONS RM_OLD_TEMPFILES
421 EMERGE_OPTIONS+=("$@")
426 [[ ${1:--} = -* ]] && break
428 if [[ ${args[0]} = --* ]]; then
429 get_longopts "${args[@]}"
431 get_shortopts "${args[@]}"
433 *) die_invalid_option "$1";;
439 normalize_emerge_opts
441 # If the user is not super, add --pretend to EMERGE_OPTIONS
442 if [[ ${EMERGE_OPTIONS[@]} != *--pretend* && $UID -ne 0 ]]; then
443 ewarn "You are not superuser. Adding --pretend to emerge options."
444 EMERGE_OPTIONS+=(--pretend)
448 # Is there a --pretend or --fetchonly flag in the EMERGE_OPTIONS array?
450 [[ ${EMERGE_OPTIONS[@]} != *--pretend* &&
451 ${EMERGE_OPTIONS[@]} != *--fetchonly* ]]
454 # Clean up temporary files and exit
457 die 1 " ...terminated. Removing incomplete $@."
462 trap "cleanup_and_die $*" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
466 # Returns 0 if the first arg is found in the remaining args, 1 otherwise
467 # (Returns 2 if given fewer than 2 arguments)
469 (( $# > 1 )) || return 2
470 local IFS=$'\a' target="$1"
472 [[ $'\a'"$*"$'\a' = *$'\a'$target$'\a'* ]]
475 # Dies when it can't change directories
477 if builtin cd -P "$@"; then
478 if [[ $1 != $PWD ]]; then
479 # Some symlink malfeasance is going on
480 die 1 "Working directory expected to be $1, but it is $PWD"
483 die 1 "Unable to change working directory to '$@'"
487 # Tries not to delete any files or directories it shouldn't
490 # Anything in the FILES array in tmpdir is fair game for removal
493 [[ $APP_NAME ]] || die 1 '$APP_NAME is not defined! (This is a bug.)'
495 */*|*-r*|*-R*) die 1 "Oops, I'm not allowed to delete that. ($@)";;
498 # Don't delete files that are not listed in the array
499 # Allow no slashes or recursive deletes at all.
501 */*|-*r*|-*R*) :;; # Not OK
504 has "$i" "${FILES[@]}" && continue
505 die 1 "Oops, I'm not allowed to delete that. ($@)"
509 # delete this setup function so it's harmless to re-run
513 # Make our temporary files directory
514 # $1 - directory name
518 die 1 'Temporary file path is unset! (This is a bug.)'
519 elif [[ -d $1 ]]; then
522 die 1 "Unable to find a satisfactory location for temporary files ($1)"
524 [[ $VERBOSE ]] && einfo "Temporary cache files are located in $PWD"
530 local uid=$(python -c 'import os; import pwd; print pwd.getpwuid(os.getuid())[0]')
531 # Find a place to put temporary files
532 if [[ "$uid" == "root" ]]; then
533 local tmp_target="/var/cache/${APP_NAME}"
535 local tmp_target="$(mktemp -d -t revdep-rebuild.XXXXXXXXXX)"
538 # From here on all work is done inside the temporary directory
539 verify_tmpdir "$tmp_target"
540 WORKING_DIR="$tmp_target"
542 if [[ $SEARCH_BROKEN ]]; then
543 SONAME_SEARCH="$SONAME"
544 HEAD_TEXT="broken by a package update"
545 OK_TEXT="Dynamic linking on your system is consistent"
546 WORKING_TEXT="consistency"
548 # first case is needed to test against /path/to/foo.so
549 if [[ $SONAME = /* ]]; then
550 # Set to "<space>$SONAME<space>"
551 SONAME_SEARCH=" $SONAME "
552 # Escape the "/" characters
553 SONAME_SEARCH="${SONAME_SEARCH//\//\\/}"
555 # Set to "<tab>$SONAME<space>"
556 SONAME_SEARCH=$'\t'"$SONAME "
558 HEAD_TEXT="using $SONAME"
559 OK_TEXT="There are no dynamic links to $SONAME"
563 # If any of our temporary files are older than 1 day, remove them all
564 if [[ ! $KEEP_TEMP ]]; then
568 done < <(find -L . -maxdepth 1 -type f -name '*.rr' -mmin +1440 -print 2>/dev/null)
571 # Compare old and new environments
572 # Don't use our previous files if environment doesn't match
574 # We do not care if these emerge options change
575 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--pretend/})
576 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--fetchonly/})
577 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--verbose/})
579 SEARCH_DIRS="$SEARCH_DIRS"
580 SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK"
581 LD_LIBRARY_MASK="$LD_LIBRARY_MASK"
582 PORTAGE_ROOT="$PORTAGE_ROOT"
583 EMERGE_OPTIONS="${EMERGE_OPTIONS[@]}"
584 ORDER_PKGS="$ORDER_PKGS"
585 FULL_LD_PATH="$FULL_LD_PATH"
588 if [[ -r "$ENV_FILE" && -s "$ENV_FILE" ]]; then
589 old_env=$(<"$ENV_FILE")
590 if [[ $old_env != $new_env ]]; then
591 ewarn 'Environment mismatch from previous run, deleting temporary files...'
595 # No env file found, silently delete any other tempfiles that may exist
599 # If we should remove old tempfiles, do so
600 if [[ $RM_OLD_TEMPFILES ]]; then
603 for file in "${FILES[@]}"; do
604 if [ -e "$file" ]; then
605 chown ${uid}:portage "$file"
611 # Save the environment in a file for next time
612 echo "$new_env" > "$ENV_FILE"
614 [[ $VERBOSE ]] && echo $'\n'"$APP_NAME environment:"$'\n'"$new_env"
617 einfo "Checking reverse dependencies"
618 einfo "Packages containing binaries and libraries $HEAD_TEXT"
619 einfo "will be emerged."
623 einfo "Collecting system binaries and libraries"
624 if [[ -r "$FILES_FILE" && -s "$FILES_FILE" ]]; then
625 einfo "Found existing $FILES_FILE"
627 # Be safe and remove any extraneous temporary files
628 # Don't remove 0_env.rr - The first file in the array
629 rm -f "${FILES[@]:1}"
631 clean_trap "$FILES_FILE"
633 if [[ $SEARCH_DIRS_MASK ]]; then
634 findMask=($SEARCH_DIRS_MASK)
635 findMask="${findMask[@]/#/-o -path }"
636 findMask="( ${findMask#-o } ) -prune -o"
638 # TODO: Check this -- afaict SEARCH_DIRS isn't an array, so this should just be $SEARCH_DIRS?
639 find ${SEARCH_DIRS[@]} $findMask -type f \( -perm -u+x -o -perm -g+x -o -perm -o+x -o \
640 -name '*.so' -o -name '*.so.*' -o -name '*.la' \) -print 2> /dev/null |
641 sort -u > "$FILES_FILE" ||
642 die $? "find failed to list binary files (This is a bug.)"
643 einfo "Generated new $FILES_FILE"
647 local COMPLETE_LD_LIBRARY_PATH
648 [[ $SEARCH_BROKEN && $FULL_LD_PATH ]] || return
649 einfo 'Collecting complete LD_LIBRARY_PATH'
650 if [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]]; then
651 einfo "Found existing $LDPATH_FILE."
653 clean_trap "$LDPATH_FILE"
654 # Ensure that the "trusted" lib directories are at the start of the path
655 COMPLETE_LD_LIBRARY_PATH=(
658 $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf)
659 $(sed 's:/[^/]*$::' < "$FILES_FILE" | sort -ru)
662 COMPLETE_LD_LIBRARY_PATH="${COMPLETE_LD_LIBRARY_PATH[*]}"
664 echo "$COMPLETE_LD_LIBRARY_PATH" > "$LDPATH_FILE"
665 einfo "Generated new $LDPATH_FILE"
675 local COMPLETE_LD_LIBRARY_PATH
676 if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
677 [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]] ||
678 die 1 "Unable to find $LDPATH_FILE"
679 COMPLETE_LD_LIBRARY_PATH=$(<"$LDPATH_FILE")
681 einfo "Checking dynamic linking $WORKING_TEXT"
682 if [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]]; then
683 einfo "Found existing $BROKEN_FILE."
685 clean_trap "$BROKEN_FILE" "$ERRORS_FILE"
686 files=($(<"$FILES_FILE"))
687 numFiles="${#files[@]}"
688 for target_file in "${files[@]}"; do
689 if [[ $target_file != *.la ]]; then
690 # Note: double checking seems to be faster than single with complete path
691 # (special add ons are rare).
692 ldd_output=$(ldd "$target_file" 2>> "$ERRORS_FILE" | sort -u)
693 ldd_status=$? # TODO: Check this for problems with sort
694 # HACK: if LD_LIBRARY_MASK is null or undefined grep -vF doesn't work
695 if grep -vF "${LD_LIBRARY_MASK:=$'\a'}" <<< "$ldd_output" |
696 grep -q "$SONAME_SEARCH"; then
697 if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
698 if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$target_file" 2>/dev/null |
699 grep -vF "$LD_LIBRARY_MASK" | grep -q "$SONAME_SEARCH"; then
700 # FIXME: I hate duplicating code
701 # Only build missing direct dependencies
703 expr='s/[[:space:]]*\([^[:space:]]*\) => not found/\1/p'
704 sed -n "$expr" <<< "$ldd_output"
707 expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
708 objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
710 MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
711 if [[ $MISSING_LIBS ]]; then
712 echo "obj $target_file" >> "$BROKEN_FILE"
713 echo_v " broken $target_file (requires $MISSING_LIBS)"
717 # FIXME: I hate duplicating code
718 # Only rebuild for direct dependencies
720 expr="/$SONAME_SEARCH/s/^[[:space:]]*\([^[:space:]]*\).*$/\1/p"
721 sort -u <<< "$ldd_output" | sed -n "$expr"
724 expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
725 objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
727 MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
728 if [[ $MISSING_LIBS ]]; then
729 echo "obj $target_file" >> "$BROKEN_FILE"
730 if [[ $SEARCH_BROKEN ]]; then
731 echo_v " broken $target_file (requires $MISSING_LIBS)"
733 echo_v " found $target_file"
738 elif [[ $SEARCH_BROKEN ]]; then
739 # Look for broken .la files
740 la_SEARCH_DIRS="$SEARCH_DIRS"
745 gawk -F"[=']" '/^dependency_libs/{
749 if [[ $depend = /* && ! -e $depend ]]; then
750 echo "obj $target_file" >> "$BROKEN_FILE"
751 echo_v " broken $target_file (requires $depend)"
752 elif [[ $depend = -[LR]/* ]]; then
753 if ! [[ $'\n'${la_SEARCH_DIRS}$'\n' == *$'\n'${depend#-?}$'\n'* ]]; then
754 la_SEARCH_DIRS+=$'\n'"${depend#-?}"
756 elif [[ $depend = "-l"* ]]; then
757 la_lib="lib${depend#-l}"
760 for la_search_dir in $la_SEARCH_DIRS; do
761 if [[ -e ${la_search_dir}/${la_lib}.so || -e ${la_search_dir}/${la_lib}.a ]]; then
766 if [[ $la_broken = yes ]]; then
767 echo "obj $target_file" >> "$BROKEN_FILE"
768 echo_v " broken $target_file (requires $depend)"
772 unset la_SEARCH_DIRS la_search_dir la_broken la_lib
775 progress $((++i)) $numFiles $target_file ||
776 progress $((++i)) $numFiles
778 if [[ $SEARCH_BROKEN ]]; then
779 # Look for missing version
780 while read target_file; do
781 echo "obj $target_file" >> "$BROKEN_FILE"
782 echo_v " broken $target_file (no version information available)"
784 # Regexify LD_LIBRARY_MASK. Exclude it from the search.
785 LD_LIBRARY_MASK="${LD_LIBRARY_MASK//$'\n'/|}"
786 gawk -v ldmask="(${LD_LIBRARY_MASK//./\\\.})" '
787 /no version information available/ && $0 !~ ldmask {
788 gsub(/[()]/, "", $NF)
789 if (seen[$NF]++) next
794 [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]] || clean_exit
795 sort -u "$BROKEN_FILE" -o "$BROKEN_FILE"
796 einfo "Generated new $BROKEN_FILE"
804 einfo 'Assigning files to packages'
805 if [[ -r "$RAW_FILE" && -s "$RAW_FILE" ]]; then
806 einfo "Found existing $RAW_FILE"
808 clean_trap "$RAW_FILE" "$OWNERS_FILE"
809 while read obj target_file; do
810 EXACT_PKG=$(get_file_owner $target_file)
811 if [[ $EXACT_PKG ]]; then
812 # Strip version information
813 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
815 echo "$EXACT_PKG" >> "$RAW_FILE"
816 echo "$target_file -> $EXACT_PKG" >> "$OWNERS_FILE"
817 echo_v " $target_file -> $PKG"
819 ewarn " !!! $target_file not owned by any package is broken !!!"
820 echo "$target_file -> (none)" >> "$OWNERS_FILE"
821 echo_v " $target_file -> (none)"
823 done < "$BROKEN_FILE"
824 einfo "Generated new $RAW_FILE and $OWNERS_FILE"
826 # if we find '(none)' on every line, exit out
827 if ! grep -qvF '(none)' "$OWNERS_FILE"; then
828 ewarn "Found some broken files, but none of them were associated with known packages"
829 ewarn "Unable to proceed with automatic repairs."
830 ewarn "The broken files are listed in $OWNERS_FILE"
831 if [[ $VERBOSE ]]; then
832 ewarn "The broken files are:"
833 while read filename junk; do
835 done < "$OWNERS_FILE"
837 exit 0 # FIXME: Should we exit 1 here?
841 einfo 'Cleaning list of packages to rebuild'
842 if [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
843 einfo "Found existing $PKGS_FILE"
845 sort -u "$RAW_FILE" > "$PKGS_FILE"
846 einfo "Generated new $PKGS_FILE"
849 assign_packages_to_ebuilds() {
853 einfo 'Assigning packages to ebuilds'
854 if [[ -r "$EBUILDS_FILE" && -s "$EBUILDS_FILE" ]]; then
855 einfo "Found existing $EBUILDS_FILE"
856 elif [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
857 clean_trap "$EBUILDS_FILE"
858 while read EXACT_PKG; do
860 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
862 SLOT=$(</var/db/pkg/$EXACT_PKG/SLOT)
864 done < "$PKGS_FILE" > "$EBUILDS_FILE"
865 einfo "Generated new $EBUILDS_FILE"
867 einfo 'Nothing to rebuild.'
868 die 1 '(The program should have already quit, so this is a minor bug.)'
871 get_exact_ebuilds() {
872 einfo 'Assigning files to ebuilds'
873 if [[ -r $EBUILDS_FILE && -s $EBUILDS_FILE ]]; then
874 einfo "Found existing $EBUILDS_FILE"
875 elif [[ -r $BROKEN_FILE && -s $BROKEN_FILE ]]; then
876 rebuildList=" $(<"$BROKEN_FILE") "
877 rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ })
878 get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$EBUILDS_FILE"
879 einfo "Generated new $EBUILDS_FILE"
881 einfo 'Nothing to rebuild.'
882 die 1 '(The program should have already quit, so this is a minor bug.)'
885 list_skipped_packages() {
887 ewarn 'Portage could not find any version of the following packages it could build:'
888 ewarn "${SKIP_LIST[@]}"
890 ewarn '(Perhaps they are masked, blocked, or removed from portage.)'
891 ewarn 'Try to emerge them manually.'
895 local -r OLD_EMERGE_DEFAULT_OPTS="$EMERGE_DEFAULT_OPTS"
896 local RAW_REBUILD_LIST
899 if [[ ! $ORDER_PKGS ]]; then
900 einfo 'Skipping package ordering'
903 einfo 'Evaluating package order'
904 if [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]]; then
905 einfo "Found existing $ORDER_FILE"
907 clean_trap "$ORDER_FILE"
908 RAW_REBUILD_LIST=$(<"$EBUILDS_FILE")
909 if [[ $RAW_REBUILD_LIST ]]; then
910 export EMERGE_DEFAULT_OPTS="--nospinner --pretend --oneshot --quiet"
911 RAW_REBUILD_LIST=($RAW_REBUILD_LIST) # convert into array
912 # If PACKAGE_NAMES is defined we're using slots, not versions
913 if [[ $PACKAGE_NAMES ]]; then
914 # Eliminate atoms that can't be built
915 for i in "${!RAW_REBUILD_LIST[@]}"; do
916 if [[ "${RAW_REBUILD_LIST[i]}" = *[A-Za-z]* ]]; then
917 portageq best_visible "$PORTAGE_ROOT" "${RAW_REBUILD_LIST[i]}" >/dev/null && continue
918 SKIP_LIST+=("${RAW_REBUILD_LIST[i]}")
920 unset RAW_REBUILD_LIST[i]
922 # If RAW_REBUILD_LIST is empty, then we have nothing to build.
923 if (( ${#RAW_REBUILD_LIST[@]} == 0 )); then
924 if (( ${#SKIP_LIST[@]} == 0 )); then
925 ewarn "The list of packages to skip is empty, but there are no"
926 ewarn "packages listed to rebuild either. (This is a bug.)"
928 list_skipped_packages
930 die 1 'Warning: Portage cannot rebuild any of the necessary packages.'
933 RAW_REBUILD_LIST="${RAW_REBUILD_LIST[@]}"
934 REBUILD_GREP=$(emerge --nodeps $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g')
935 if (( ${PIPESTATUS[0]} == 0 )); then
936 emerge --deep $RAW_REBUILD_LIST |
937 sed 's/\[[^]]*\]//g' |
938 grep -F "$REBUILD_GREP" > "$ORDER_FILE"
941 # Here we use the PIPESTATUS from the second emerge, the --deep one.
942 if (( ${PIPESTATUS[0]} != 0 )); then
944 eerror 'Warning: Failed to resolve package order.'
945 eerror 'Will merge in arbitrary order'
949 - An ebuild is no longer in the portage tree.
950 - An ebuild is masked, use /etc/portage/packages.keyword
951 and/or /etc/portage/package.unmask to unmask it
956 export EMERGE_DEFAULT_OPTS="$OLD_EMERGE_DEFAULT_OPTS"
958 einfo 'Nothing to rebuild.'
959 die 1 '(The program should have already quit, so this is a minor bug.)'
962 [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]] && einfo "Generated new $ORDER_FILE"
965 show_unowned_files() {
966 if grep -qF '(none)' "$OWNERS_FILE"; then
967 ewarn "Found some broken files that weren't associated with known packages"
968 ewarn "The broken files are:"
969 while read filename junk; do
970 [[ $junk = *none* ]] && ewarn " $filename"
971 done < "$OWNERS_FILE" | gawk '!s[$0]++' # (omit dupes)
975 # Get multiple portage variables at once to speedup revdep-rebuild.
991 results=( $(unset SEARCH_DIRS; unset SEARCH_DIRS_MASK; unset LD_LIBRARY_MASK; portageq envvar ${query_vars[*]}) )
994 PORTAGE_ROOT=${results[0]}
995 PORTAGE_NICENESS=${results[1]}
996 EMERGE_DEFAULT_OPTS=${results[2]}
997 export NOCOLOR=${results[3]}
998 SEARCH_DIRS+=" "${results[4]}
999 SEARCH_DIRS_MASK+=" "${results[5]}
1000 LD_LIBRARY_MASK+=" "${results[6]}
1004 # Setup portage and the search paths
1006 # Obey PORTAGE_NICENESS
1007 if [[ $PORTAGE_NICENESS ]]; then
1008 renice $PORTAGE_NICENESS $$ > /dev/null
1009 # Since we have already set our nice value for our processes,
1010 # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
1011 export PORTAGE_NICENESS="0"
1014 PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
1018 # Setup the paths to search (and filter the ones to avoid)
1019 setup_search_paths_and_masks() {
1020 local configfile sdir mdir skip_me filter_SEARCH_DIRS
1022 einfo "Configuring search environment for $APP_NAME"
1024 # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
1025 # portage, and the environment
1027 # Read the incremental variables from environment and portage
1028 # Until such time as portage supports these variables as incrementals
1029 # The value will be what is in /etc/make.conf
1030 # SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
1031 # SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
1032 # LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
1035 if [[ -d /etc/revdep-rebuild ]]; then
1036 for configfile in /etc/revdep-rebuild/*; do
1037 SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS)
1038 SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK)
1039 LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK)
1042 SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
1043 SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
1044 LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
1047 # Get the ROOTPATH and PATH from /etc/profile.env
1048 if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
1049 SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
1052 # Get the directories from /etc/ld.so.conf
1053 if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
1054 SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
1057 # Set the final variables
1058 SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
1059 SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
1060 LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
1061 # Filter masked paths from SEARCH_DIRS
1062 for sdir in ${SEARCH_DIRS} ; do
1064 for mdir in ${SEARCH_DIRS_MASK}; do
1065 [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break
1067 [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
1069 SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}")
1070 [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
1073 # Rebuild packages owning broken binaries
1075 if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
1076 REBUILD_LIST=( $(<"$LIST.5_order") )
1077 REBUILD_LIST="${REBUILD_LIST[@]/#/=}"
1079 REBUILD_LIST=$(sort -u "$EBUILDS_FILE")
1082 trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1084 einfo 'All prepared. Starting rebuild'
1085 echo "emerge --oneshot ${EMERGE_OPTIONS[@]} ${EMERGE_DEFAULT_OPTS} $REBUILD_LIST"
1087 is_real_merge && countdown 10
1089 # Link file descriptor #6 with stdin so --ask will work
1092 # Run in background to correctly handle Ctrl-C
1094 emerge --oneshot ${EMERGE_OPTIONS[@]} ${EMERGE_DEFAULT_OPTS} $REBUILD_LIST <&6
1095 echo $? > "$STATUS_FILE"
1099 # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
1105 if (( $(<"$STATUS_FILE") != 0 )); then
1107 ewarn "$APP_NAME failed to emerge all packages."
1108 ewarn 'you have the following choices:'
1109 einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
1110 einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
1111 einfo " (and remove $ORDER_FILE to be evaluated again)"
1112 einfo '- Modify the above emerge command and run it manually.'
1113 einfo '- Compile or unmerge unsatisfied packages manually,'
1114 einfo ' remove temporary files, and try again.'
1115 einfo ' (you can edit package/ebuild list first)'
1117 einfo 'To remove temporary files, please run:'
1118 einfo "rm ${WORKING_DIR}/*.rr"
1121 elif is_real_merge; then
1123 eerror "terminated. Please remove the temporary files manually:"
1124 eerror "rm ${WORKING_DIR}/*.rr"
1127 [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages
1128 trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1129 einfo 'Build finished correctly. Removing temporary files...'
1131 einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
1132 einfo 'are fixed. Possible reasons for remaining inconsistencies include:'
1133 einfo ' orphaned files'
1134 einfo ' deep dependencies'
1135 einfo " packages installed outside of portage's control"
1136 einfo ' specially-evaluated libraries'
1137 if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then
1140 [[ $KEEP_TEMP ]] || rm "${FILES[@]}"
1142 einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'