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!
266 # Add a space to the end of each object name to prevent false
267 # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
268 # The same for "${rpath} ".
270 rpath=$(realpath "${*}" 2>/dev/null)
271 # To ensure we always have something in rpath...
272 [[ -z $rpath ]] && rpath=${*}
274 find -L /var/db/pkg -name CONTENTS -print0 |
275 xargs -0 grep -Fl -e "${*} " -e "${rpath} " |
276 sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
279 # Normalize some EMERGE_OPTIONS
280 normalize_emerge_opts() {
281 # Normalize some EMERGE_OPTIONS
282 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
283 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
284 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-v/--verbose})
287 # Use the color preference from portage
289 # This should still work if NOCOLOR is set by the -C flag or in the user's
291 [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr)
292 . /etc/init.d/functions.sh
295 # Die if an argument is missing.
296 die_if_missing_arg() {
297 [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1"
300 # Die because an option is not recognized.
301 die_invalid_option() {
302 # Can't use eerror and einfo because this gets called before function.sh
305 echo "Encountered unrecognized option $1." >&2
307 echo "$APP_NAME no longer automatically passes unrecognized options to portage."
308 echo "Separate emerge-only options from revdep-rebuild options with the -- flag."
310 echo "For example, $APP_NAME -v -- --ask"
312 echo "See the man page or $APP_NAME -h for more detail."
317 # Warn about deprecated options.
318 warn_deprecated_opt() {
319 # Can't use eerror and einfo because this gets called before function.sh
322 echo "Encountered deprecated option $1." >&2
323 [[ $2 ]] && echo "Please use $2 instead." >&2
326 # Get whole-word commandline options preceded by two dashes.
329 --nocolor) export NOCOLOR="yes";;
330 --no-color) warn_deprecated_opt "$1" "--nocolor"
331 export NOCOLOR="yes";;
333 --exact) unset PACKAGE_NAMES;;
336 --ignore) RM_OLD_TEMPFILES=1;;
337 --keep-temp) KEEP_TEMP=1;;
338 --library=*) # TODO: check for invalid values
340 unset SEARCH_BROKEN;;
341 --soname=*|--soname-regexp=*) # TODO: check for invalid values
342 warn_deprecated_opt "${1%=*}" "--library"
344 unset SEARCH_BROKEN;;
345 --library) # TODO: check for invalid values
346 die_if_missing_arg $1 $2
349 unset SEARCH_BROKEN;;
350 --soname|--soname-regexp) # TODO: check for invalid values
351 warn_deprecated_opt "$1" "--library"
352 die_if_missing_arg $1 $2
355 unset SEARCH_BROKEN;;
356 --no-ld-path) unset FULL_LD_PATH;;
357 --no-order) unset ORDER_PKGS;;
358 --no-progress) progress() { :; };;
359 --pretend) EMERGE_OPTIONS+=("--pretend");;
360 --quiet) echo_v() { :; }
363 EMERGE_OPTIONS+=($1);;
365 EMERGE_OPTIONS+=("--verbose");;
366 --extra-verbose) warn_deprecated_opt "$1" "--verbose"
368 EMERGE_OPTIONS+=("--verbose");;
369 --package-names) # No longer used, since it is the
370 # default. We accept it for
371 # backwards compatibility.
372 warn_deprecated_opt "$1"
374 *) die_invalid_option $1;;
379 # Get single-letter commandline options preceded by a single dash.
381 local OPT OPTSTRING OPTARG OPTIND
382 while getopts ":CdehikL:loPpqu:vX" OPT; do
384 C) # TODO: Match syntax with the rest of gentoolkit
385 export NOCOLOR="yes";;
387 e) unset PACKAGE_NAMES;;
390 i) RM_OLD_TEMPFILES=1;;
392 L) # TODO: Check for invalid values
393 SONAME="${OPTARG#*=}"
394 unset SEARCH_BROKEN;;
395 l) unset FULL_LD_PATH;;
396 o) unset ORDER_PKGS;;
397 P) progress() { :; };;
398 p) EMERGE_OPTIONS+=("--pretend");;
402 EMERGE_OPTIONS+=("--quiet");;
404 EMERGE_OPTIONS+=("--verbose");;
405 X) # No longer used, since it is the default.
406 # We accept it for backwards compatibility.
407 warn_deprecated_opt "-X"
409 *) die_invalid_option "-$OPTARG";;
414 # Get command-line options.
418 echo_v() { ewarn "$@"; }
419 unset VERBOSE KEEP_TEMP EMERGE_OPTIONS RM_OLD_TEMPFILES
428 EMERGE_OPTIONS+=("$@")
433 [[ ${1:--} = -* ]] && break
435 if [[ ${args[0]} = --* ]]; then
436 get_longopts "${args[@]}"
438 get_shortopts "${args[@]}"
440 *) die_invalid_option "$1";;
446 normalize_emerge_opts
448 # If the user is not super, add --pretend to EMERGE_OPTIONS
449 if [[ ${EMERGE_OPTIONS[@]} != *--pretend* && $UID -ne 0 ]]; then
450 ewarn "You are not superuser. Adding --pretend to emerge options."
451 EMERGE_OPTIONS+=(--pretend)
455 # Is there a --pretend or --fetchonly flag in the EMERGE_OPTIONS array?
457 [[ ${EMERGE_OPTIONS[@]} != *--pretend* &&
458 ${EMERGE_OPTIONS[@]} != *--fetchonly* ]]
461 # Clean up temporary files and exit
464 die 1 " ...terminated. Removing incomplete $@."
469 trap "cleanup_and_die $*" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
473 # Returns 0 if the first arg is found in the remaining args, 1 otherwise
474 # (Returns 2 if given fewer than 2 arguments)
476 (( $# > 1 )) || return 2
477 local IFS=$'\a' target="$1"
479 [[ $'\a'"$*"$'\a' = *$'\a'$target$'\a'* ]]
482 # Dies when it can't change directories
484 if builtin cd -P "$@"; then
485 if [[ $1 != $PWD ]]; then
486 # Some symlink malfeasance is going on
487 die 1 "Working directory expected to be $1, but it is $PWD"
490 die 1 "Unable to change working directory to '$@'"
494 # Tries not to delete any files or directories it shouldn't
497 # Anything in the FILES array in tmpdir is fair game for removal
500 [[ $APP_NAME ]] || die 1 '$APP_NAME is not defined! (This is a bug.)'
502 */*|*-r*|*-R*) die 1 "Oops, I'm not allowed to delete that. ($@)";;
505 # Don't delete files that are not listed in the array
506 # Allow no slashes or recursive deletes at all.
508 */*|-*r*|-*R*) :;; # Not OK
511 has "$i" "${FILES[@]}" && continue
512 die 1 "Oops, I'm not allowed to delete that. ($@)"
516 # delete this setup function so it's harmless to re-run
520 # Make our temporary files directory
521 # $1 - directory name
525 die 1 'Temporary file path is unset! (This is a bug.)'
526 elif [[ -d $1 ]]; then
529 die 1 "Unable to find a satisfactory location for temporary files ($1)"
531 [[ $VERBOSE ]] && einfo "Temporary cache files are located in $PWD"
537 local uid=$(python -c 'import os; import pwd; print pwd.getpwuid(os.getuid())[0]')
538 # Find a place to put temporary files
539 if [[ "$uid" == "root" ]]; then
540 local tmp_target="/var/cache/${APP_NAME}"
542 local tmp_target="$(mktemp -d -t revdep-rebuild.XXXXXXXXXX)"
545 # From here on all work is done inside the temporary directory
546 verify_tmpdir "$tmp_target"
547 WORKING_DIR="$tmp_target"
549 if [[ $SEARCH_BROKEN ]]; then
550 SONAME_SEARCH="$SONAME"
551 HEAD_TEXT="broken by a package update"
552 OK_TEXT="Dynamic linking on your system is consistent"
553 WORKING_TEXT="consistency"
555 # first case is needed to test against /path/to/foo.so
556 if [[ $SONAME = /* ]]; then
557 # Set to "<space>$SONAME<space>"
558 SONAME_SEARCH=" $SONAME "
559 # Escape the "/" characters
560 SONAME_SEARCH="${SONAME_SEARCH//\//\\/}"
562 # Set to "<tab>$SONAME<space>"
563 SONAME_SEARCH=$'\t'"$SONAME "
565 HEAD_TEXT="using $SONAME"
566 OK_TEXT="There are no dynamic links to $SONAME"
570 # If any of our temporary files are older than 1 day, remove them all
571 if [[ ! $KEEP_TEMP ]]; then
575 done < <(find -L . -maxdepth 1 -type f -name '*.rr' -mmin +1440 -print 2>/dev/null)
578 # Compare old and new environments
579 # Don't use our previous files if environment doesn't match
581 # We do not care if these emerge options change
582 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--pretend/})
583 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--fetchonly/})
584 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--verbose/})
586 SEARCH_DIRS="$SEARCH_DIRS"
587 SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK"
588 LD_LIBRARY_MASK="$LD_LIBRARY_MASK"
589 PORTAGE_ROOT="$PORTAGE_ROOT"
590 EMERGE_OPTIONS="${EMERGE_OPTIONS[@]}"
591 ORDER_PKGS="$ORDER_PKGS"
592 FULL_LD_PATH="$FULL_LD_PATH"
595 if [[ -r "$ENV_FILE" && -s "$ENV_FILE" ]]; then
596 old_env=$(<"$ENV_FILE")
597 if [[ $old_env != $new_env ]]; then
598 ewarn 'Environment mismatch from previous run, deleting temporary files...'
602 # No env file found, silently delete any other tempfiles that may exist
606 # If we should remove old tempfiles, do so
607 if [[ $RM_OLD_TEMPFILES ]]; then
610 for file in "${FILES[@]}"; do
611 if [ -e "$file" ]; then
612 chown ${uid}:portage "$file"
618 # Save the environment in a file for next time
619 echo "$new_env" > "$ENV_FILE"
621 [[ $VERBOSE ]] && echo $'\n'"$APP_NAME environment:"$'\n'"$new_env"
624 einfo "Checking reverse dependencies"
625 einfo "Packages containing binaries and libraries $HEAD_TEXT"
626 einfo "will be emerged."
630 einfo "Collecting system binaries and libraries"
631 if [[ -r "$FILES_FILE" && -s "$FILES_FILE" ]]; then
632 einfo "Found existing $FILES_FILE"
634 # Be safe and remove any extraneous temporary files
635 # Don't remove 0_env.rr - The first file in the array
636 rm -f "${FILES[@]:1}"
638 clean_trap "$FILES_FILE"
640 if [[ $SEARCH_DIRS_MASK ]]; then
641 findMask=($SEARCH_DIRS_MASK)
642 findMask="${findMask[@]/#/-o -path }"
643 findMask="( ${findMask#-o } ) -prune -o"
645 # TODO: Check this -- afaict SEARCH_DIRS isn't an array, so this should just be $SEARCH_DIRS?
646 find ${SEARCH_DIRS[@]} $findMask -type f \( -perm -u+x -o -perm -g+x -o -perm -o+x -o \
647 -name '*.so' -o -name '*.so.*' -o -name '*.la' \) -print 2> /dev/null |
648 sort -u > "$FILES_FILE" ||
649 die $? "find failed to list binary files (This is a bug.)"
650 einfo "Generated new $FILES_FILE"
654 local COMPLETE_LD_LIBRARY_PATH
655 [[ $SEARCH_BROKEN && $FULL_LD_PATH ]] || return
656 einfo 'Collecting complete LD_LIBRARY_PATH'
657 if [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]]; then
658 einfo "Found existing $LDPATH_FILE."
660 clean_trap "$LDPATH_FILE"
661 # Ensure that the "trusted" lib directories are at the start of the path
662 COMPLETE_LD_LIBRARY_PATH=(
665 $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf)
666 $(sed 's:/[^/]*$::' < "$FILES_FILE" | sort -ru)
669 COMPLETE_LD_LIBRARY_PATH="${COMPLETE_LD_LIBRARY_PATH[*]}"
671 echo "$COMPLETE_LD_LIBRARY_PATH" > "$LDPATH_FILE"
672 einfo "Generated new $LDPATH_FILE"
682 local COMPLETE_LD_LIBRARY_PATH
683 if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
684 [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]] ||
685 die 1 "Unable to find $LDPATH_FILE"
686 COMPLETE_LD_LIBRARY_PATH=$(<"$LDPATH_FILE")
688 einfo "Checking dynamic linking $WORKING_TEXT"
689 if [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]]; then
690 einfo "Found existing $BROKEN_FILE."
692 clean_trap "$BROKEN_FILE" "$ERRORS_FILE"
693 files=($(<"$FILES_FILE"))
694 numFiles="${#files[@]}"
695 for target_file in "${files[@]}"; do
696 if [[ $target_file != *.la ]]; then
697 # Note: double checking seems to be faster than single with complete path
698 # (special add ons are rare).
699 ldd_output=$(ldd "$target_file" 2>> "$ERRORS_FILE" | sort -u)
700 ldd_status=$? # TODO: Check this for problems with sort
701 # HACK: if LD_LIBRARY_MASK is null or undefined grep -vF doesn't work
702 if grep -vF "${LD_LIBRARY_MASK:=$'\a'}" <<< "$ldd_output" |
703 grep -q "$SONAME_SEARCH"; then
704 if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
705 if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$target_file" 2>/dev/null |
706 grep -vF "$LD_LIBRARY_MASK" | grep -q "$SONAME_SEARCH"; then
707 # FIXME: I hate duplicating code
708 # Only build missing direct dependencies
710 expr='s/[[:space:]]*\([^[:space:]]*\) => not found/\1/p'
711 sed -n "$expr" <<< "$ldd_output"
714 expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
715 objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
717 MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
718 if [[ $MISSING_LIBS ]]; then
719 echo "obj $target_file" >> "$BROKEN_FILE"
720 echo_v " broken $target_file (requires $MISSING_LIBS)"
724 # FIXME: I hate duplicating code
725 # Only rebuild for direct dependencies
727 expr="/$SONAME_SEARCH/s/^[[:space:]]*\([^[:space:]]*\).*$/\1/p"
728 sort -u <<< "$ldd_output" | sed -n "$expr"
731 expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
732 objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
734 MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
735 if [[ $MISSING_LIBS ]]; then
736 echo "obj $target_file" >> "$BROKEN_FILE"
737 if [[ $SEARCH_BROKEN ]]; then
738 echo_v " broken $target_file (requires $MISSING_LIBS)"
740 echo_v " found $target_file"
745 elif [[ $SEARCH_BROKEN ]]; then
746 # Look for broken .la files
747 la_SEARCH_DIRS="$SEARCH_DIRS"
752 gawk -F"[=']" '/^dependency_libs/{
756 if [[ $depend = /* && ! -e $depend ]]; then
757 echo "obj $target_file" >> "$BROKEN_FILE"
758 echo_v " broken $target_file (requires $depend)"
759 elif [[ $depend = -[LR]/* ]]; then
760 if ! [[ $'\n'${la_SEARCH_DIRS}$'\n' == *$'\n'${depend#-?}$'\n'* ]]; then
761 la_SEARCH_DIRS+=$'\n'"${depend#-?}"
763 elif [[ $depend = "-l"* ]]; then
764 la_lib="lib${depend#-l}"
767 for la_search_dir in $la_SEARCH_DIRS; do
768 if [[ -e ${la_search_dir}/${la_lib}.so || -e ${la_search_dir}/${la_lib}.a ]]; then
773 if [[ $la_broken = yes ]]; then
774 echo "obj $target_file" >> "$BROKEN_FILE"
775 echo_v " broken $target_file (requires $depend)"
779 unset la_SEARCH_DIRS la_search_dir la_broken la_lib
782 progress $((++i)) $numFiles $target_file ||
783 progress $((++i)) $numFiles
785 if [[ $SEARCH_BROKEN ]]; then
786 # Look for missing version
787 while read target_file; do
788 echo "obj $target_file" >> "$BROKEN_FILE"
789 echo_v " broken $target_file (no version information available)"
791 # Regexify LD_LIBRARY_MASK. Exclude it from the search.
792 LD_LIBRARY_MASK="${LD_LIBRARY_MASK//$'\n'/|}"
793 gawk -v ldmask="(${LD_LIBRARY_MASK//./\\\.})" '
794 /no version information available/ && $0 !~ ldmask {
795 gsub(/[()]/, "", $NF)
796 if (seen[$NF]++) next
801 [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]] || clean_exit
802 sort -u "$BROKEN_FILE" -o "$BROKEN_FILE"
803 einfo "Generated new $BROKEN_FILE"
811 einfo 'Assigning files to packages'
812 if [[ -r "$RAW_FILE" && -s "$RAW_FILE" ]]; then
813 einfo "Found existing $RAW_FILE"
815 clean_trap "$RAW_FILE" "$OWNERS_FILE"
816 while read obj target_file; do
817 EXACT_PKG=$(get_file_owner $target_file)
818 if [[ $EXACT_PKG ]]; then
819 # Strip version information
820 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
822 echo "$EXACT_PKG" >> "$RAW_FILE"
823 echo "$target_file -> $EXACT_PKG" >> "$OWNERS_FILE"
824 echo_v " $target_file -> $PKG"
826 ewarn " !!! $target_file not owned by any package is broken !!!"
827 echo "$target_file -> (none)" >> "$OWNERS_FILE"
828 echo_v " $target_file -> (none)"
830 done < "$BROKEN_FILE"
831 einfo "Generated new $RAW_FILE and $OWNERS_FILE"
833 # if we find '(none)' on every line, exit out
834 if ! grep -qvF '(none)' "$OWNERS_FILE"; then
835 ewarn "Found some broken files, but none of them were associated with known packages"
836 ewarn "Unable to proceed with automatic repairs."
837 ewarn "The broken files are listed in $OWNERS_FILE"
838 if [[ $VERBOSE ]]; then
839 ewarn "The broken files are:"
840 while read filename junk; do
842 done < "$OWNERS_FILE"
844 exit 0 # FIXME: Should we exit 1 here?
848 einfo 'Cleaning list of packages to rebuild'
849 if [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
850 einfo "Found existing $PKGS_FILE"
852 sort -u "$RAW_FILE" > "$PKGS_FILE"
853 einfo "Generated new $PKGS_FILE"
856 assign_packages_to_ebuilds() {
860 einfo 'Assigning packages to ebuilds'
861 if [[ -r "$EBUILDS_FILE" && -s "$EBUILDS_FILE" ]]; then
862 einfo "Found existing $EBUILDS_FILE"
863 elif [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
864 clean_trap "$EBUILDS_FILE"
865 while read EXACT_PKG; do
867 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
869 SLOT=$(</var/db/pkg/$EXACT_PKG/SLOT)
871 done < "$PKGS_FILE" > "$EBUILDS_FILE"
872 einfo "Generated new $EBUILDS_FILE"
874 einfo 'Nothing to rebuild.'
875 die 1 '(The program should have already quit, so this is a minor bug.)'
878 get_exact_ebuilds() {
879 einfo 'Assigning files to ebuilds'
880 if [[ -r $EBUILDS_FILE && -s $EBUILDS_FILE ]]; then
881 einfo "Found existing $EBUILDS_FILE"
882 elif [[ -r $BROKEN_FILE && -s $BROKEN_FILE ]]; then
883 rebuildList=" $(<"$BROKEN_FILE") "
884 rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ })
885 get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$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 list_skipped_packages() {
894 ewarn 'Portage could not find any version of the following packages it could build:'
895 ewarn "${SKIP_LIST[@]}"
897 ewarn '(Perhaps they are masked, blocked, or removed from portage.)'
898 ewarn 'Try to emerge them manually.'
902 local -r OLD_EMERGE_DEFAULT_OPTS="$EMERGE_DEFAULT_OPTS"
903 local RAW_REBUILD_LIST
906 if [[ ! $ORDER_PKGS ]]; then
907 einfo 'Skipping package ordering'
910 einfo 'Evaluating package order'
911 if [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]]; then
912 einfo "Found existing $ORDER_FILE"
914 clean_trap "$ORDER_FILE"
915 RAW_REBUILD_LIST=$(<"$EBUILDS_FILE")
916 if [[ $RAW_REBUILD_LIST ]]; then
917 export EMERGE_DEFAULT_OPTS="--nospinner --pretend --oneshot --quiet"
918 RAW_REBUILD_LIST=($RAW_REBUILD_LIST) # convert into array
919 # If PACKAGE_NAMES is defined we're using slots, not versions
920 if [[ $PACKAGE_NAMES ]]; then
921 # Eliminate atoms that can't be built
922 for i in "${!RAW_REBUILD_LIST[@]}"; do
923 if [[ "${RAW_REBUILD_LIST[i]}" = *[A-Za-z]* ]]; then
924 portageq best_visible "$PORTAGE_ROOT" "${RAW_REBUILD_LIST[i]}" >/dev/null && continue
925 SKIP_LIST+=("${RAW_REBUILD_LIST[i]}")
927 unset RAW_REBUILD_LIST[i]
929 # If RAW_REBUILD_LIST is empty, then we have nothing to build.
930 if (( ${#RAW_REBUILD_LIST[@]} == 0 )); then
931 if (( ${#SKIP_LIST[@]} == 0 )); then
932 ewarn "The list of packages to skip is empty, but there are no"
933 ewarn "packages listed to rebuild either. (This is a bug.)"
935 list_skipped_packages
937 die 1 'Warning: Portage cannot rebuild any of the necessary packages.'
940 RAW_REBUILD_LIST="${RAW_REBUILD_LIST[@]}"
941 REBUILD_GREP=$(emerge --nodeps $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g')
942 if (( ${PIPESTATUS[0]} == 0 )); then
943 emerge --deep $RAW_REBUILD_LIST |
944 sed 's/\[[^]]*\]//g' |
945 grep -F "$REBUILD_GREP" > "$ORDER_FILE"
948 # Here we use the PIPESTATUS from the second emerge, the --deep one.
949 if (( ${PIPESTATUS[0]} != 0 )); then
951 eerror 'Warning: Failed to resolve package order.'
952 eerror 'Will merge in arbitrary order'
956 - An ebuild is no longer in the portage tree.
957 - An ebuild is masked, use /etc/portage/packages.keyword
958 and/or /etc/portage/package.unmask to unmask it
963 export EMERGE_DEFAULT_OPTS="$OLD_EMERGE_DEFAULT_OPTS"
965 einfo 'Nothing to rebuild.'
966 die 1 '(The program should have already quit, so this is a minor bug.)'
969 [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]] && einfo "Generated new $ORDER_FILE"
972 show_unowned_files() {
973 if grep -qF '(none)' "$OWNERS_FILE"; then
974 ewarn "Found some broken files that weren't associated with known packages"
975 ewarn "The broken files are:"
976 while read filename junk; do
977 [[ $junk = *none* ]] && ewarn " $filename"
978 done < "$OWNERS_FILE" | gawk '!s[$0]++' # (omit dupes)
982 # Get multiple portage variables at once to speedup revdep-rebuild.
998 results=( $(unset SEARCH_DIRS; unset SEARCH_DIRS_MASK; unset LD_LIBRARY_MASK; portageq envvar ${query_vars[*]}) )
1001 PORTAGE_ROOT=${results[0]}
1002 PORTAGE_NICENESS=${results[1]}
1003 EMERGE_DEFAULT_OPTS=${results[2]}
1004 export NOCOLOR=${results[3]}
1005 SEARCH_DIRS+=" "${results[4]}
1006 SEARCH_DIRS_MASK+=" "${results[5]}
1007 LD_LIBRARY_MASK+=" "${results[6]}
1011 # Setup portage and the search paths
1013 # Obey PORTAGE_NICENESS
1014 if [[ $PORTAGE_NICENESS ]]; then
1015 renice $PORTAGE_NICENESS $$ > /dev/null
1016 # Since we have already set our nice value for our processes,
1017 # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
1018 export PORTAGE_NICENESS="0"
1021 PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
1025 # Setup the paths to search (and filter the ones to avoid)
1026 setup_search_paths_and_masks() {
1027 local configfile sdir mdir skip_me filter_SEARCH_DIRS
1029 einfo "Configuring search environment for $APP_NAME"
1031 # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
1032 # portage, and the environment
1034 # Read the incremental variables from environment and portage
1035 # Until such time as portage supports these variables as incrementals
1036 # The value will be what is in /etc/make.conf
1037 # SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
1038 # SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
1039 # LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
1042 if [[ -d /etc/revdep-rebuild ]]; then
1043 for configfile in /etc/revdep-rebuild/*; do
1044 SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS)
1045 SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK)
1046 LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK)
1049 SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
1050 SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
1051 LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
1054 # Get the ROOTPATH and PATH from /etc/profile.env
1055 if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
1056 SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
1059 # Get the directories from /etc/ld.so.conf
1060 if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
1061 SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
1064 # Set the final variables
1065 SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
1066 SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
1067 LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
1068 # Filter masked paths from SEARCH_DIRS
1069 for sdir in ${SEARCH_DIRS} ; do
1071 for mdir in ${SEARCH_DIRS_MASK}; do
1072 [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break
1074 [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
1076 SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}")
1077 [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
1080 # Rebuild packages owning broken binaries
1082 if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
1083 REBUILD_LIST=( $(<"$LIST.5_order") )
1084 REBUILD_LIST="${REBUILD_LIST[@]/#/=}"
1086 REBUILD_LIST=$(sort -u "$EBUILDS_FILE")
1089 trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1091 einfo 'All prepared. Starting rebuild'
1092 echo "emerge --oneshot ${EMERGE_OPTIONS[@]} ${EMERGE_DEFAULT_OPTS} $REBUILD_LIST"
1094 is_real_merge && countdown 10
1096 # Link file descriptor #6 with stdin so --ask will work
1099 # Run in background to correctly handle Ctrl-C
1101 emerge --oneshot ${EMERGE_OPTIONS[@]} ${EMERGE_DEFAULT_OPTS} $REBUILD_LIST <&6
1102 echo $? > "$STATUS_FILE"
1106 # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
1112 if (( $(<"$STATUS_FILE") != 0 )); then
1114 ewarn "$APP_NAME failed to emerge all packages."
1115 ewarn 'you have the following choices:'
1116 einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
1117 einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
1118 einfo " (and remove $ORDER_FILE to be evaluated again)"
1119 einfo '- Modify the above emerge command and run it manually.'
1120 einfo '- Compile or unmerge unsatisfied packages manually,'
1121 einfo ' remove temporary files, and try again.'
1122 einfo ' (you can edit package/ebuild list first)'
1124 einfo 'To remove temporary files, please run:'
1125 einfo "rm ${WORKING_DIR}/*.rr"
1128 elif is_real_merge; then
1130 eerror "terminated. Please remove the temporary files manually:"
1131 eerror "rm ${WORKING_DIR}/*.rr"
1134 [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages
1135 trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1136 einfo 'Build finished correctly. Removing temporary files...'
1138 einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
1139 einfo 'are fixed. Possible reasons for remaining inconsistencies include:'
1140 einfo ' orphaned files'
1141 einfo ' deep dependencies'
1142 einfo " packages installed outside of portage's control"
1143 einfo ' specially-evaluated libraries'
1144 if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then
1147 [[ $KEEP_TEMP ]] || rm "${FILES[@]}"
1149 einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'