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
94 setup_search_paths_and_masks
98 # Search for broken binaries
103 # Associate broken binaries with packages to rebuild
104 if [[ $PACKAGE_NAMES ]]; then
107 assign_packages_to_ebuilds
112 # Rebuild packages owning broken binaries
120 # Refuse to delete anything before we cd to our tmpdir
121 # (See mkdir_and_cd_to_tmpdir()
123 eerror "I was instructed to rm '$@'"
124 die 1 "Refusing to delete anything before changing to temporary directory."
128 # GNU find has -executable, but if our users' finds do not have that flag
129 # we emulate it with this function. Also emulates -writable and -readable.
130 # Usage: find PATH ARGS -- use find like normal, except use -executable instead
131 # of various versions of -perm /+ blah blah and hacks
133 hash find || { die 1 'find not found!'; }
134 # We can be pretty sure find itself should be executable.
135 local testsubject="$(type -P find)"
136 if [[ $(command find "$testsubject" -executable 2> /dev/null) ]]; then
137 unset -f find # We can just use the command find
138 elif [[ $(command find "$testsubject" -perm /u+x 2> /dev/null) ]]; then
140 a=(${@//-executable/-perm \/u+x})
141 a=(${a[@]//-writable/-perm \/u+w})
142 a=(${a[@]//-readable/-perm \/r+w})
143 command find "${a[@]}"
145 elif [[ $(command find "$testsubject" -perm +u+x 2> /dev/null) ]]; then
147 a=(${@//-executable/-perm +u+x})
148 a=(${a[@]//-writable/-perm +u+w})
149 a=(${a[@]//-readable/-perm +r+w})
150 command find "${a[@]}"
154 a=(${@//-executable/-exec test -x '{}' \; -print})
155 a=(${a[@]//-writable/-exec test -w '{}' \; -print})
156 a=(${a[@]//-readable/-exec test -r '{}' \; -print})
157 command find "${a[@]}"
166 Usage: $APP_NAME [OPTIONS] [--] [EMERGE_OPTIONS]
168 Broken reverse dependency rebuilder.
170 -C, --nocolor Turn off colored output
171 -d, --debug Print way too much information (uses bash's set -xv)
172 -e, --exact Emerge based on exact package version
173 -h, --help Print this usage
174 -i, --ignore Ignore temporary files from previous runs
175 -k, --keep-temp Do not delete temporary files on exit
176 -L, --library NAME Emerge existing packages that use the library with NAME
177 --library=NAME NAME can be a full path to the library or a basic
178 regular expression (man grep)
179 -l, --no-ld-path Do not set LD_LIBRARY_PATH
180 -o, --no-order Do not check the build order
181 (Saves time, but may cause breakage.)
182 -p, --pretend Do a trial run without actually emerging anything
183 (also passed to emerge command)
184 -P, --no-progress Turn off the progress meter
185 -q, --quiet Be less verbose (also passed to emerge command)
186 -v, --verbose Be more verbose (also passed to emerge command)
188 Calls emerge, options after -- are ignored by $APP_NAME
189 and passed directly to emerge.
191 Report bugs to <http://bugs.gentoo.org>
195 # Usage: progress i n
197 # n: total number of items to process
201 local curProg=$(( $1 * 100 / $2 ))
202 (( curProg == OLDPROG )) && return # no change, output nothing
203 OLDPROG="$curProg" # must be a global variable
204 (( $1 == $2 )) && local lb=$'\n'
205 echo -ne '\r \r'"[ $curProg% ] $lb"
208 else # STDOUT is not a tty. Disable progress meter.
214 # n: number of seconds to count
217 for ((i=1; i<$1; i++)); do
224 # Replace whitespace with linebreaks, normalize repeated '/' chars, and sort -u
225 # (If any libs have whitespace in their filenames, someone needs punishment.)
227 gawk 'BEGIN {RS="[[:space:]]"}
229 /[^[:space:]]/ {gsub(/\/\/+/, "/"); print}' | sort -u
232 # Exit and optionally output to sterr
240 # What to do when dynamic linking is consistent
242 if [[ ! $KEEP_TEMP ]]; then
244 if [[ "$WORKING_DIR" != "/var/cache/${APP_NAME}" ]]; then
245 # Remove the working directory
246 builtin cd; rmdir "$WORKING_DIR"
250 einfo "$OK_TEXT... All done. "
254 # Get the name of the package that owns a file or list of files given as args.
257 # ${*/%/ } adds a space to the end of each object name to prevent false
258 # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
259 find -L /var/db/pkg -name CONTENTS -print0 |
260 xargs -0 grep -Fl "${*/%/ }" |
261 sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
264 # Normalize some EMERGE_OPTIONS
265 normalize_emerge_opts() {
266 # Normalize some EMERGE_OPTIONS
267 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
268 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
269 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--verbose})
272 # Use the color preference from portage
274 # This should still work if NOCOLOR is set by the -C flag or in the user's
276 export NOCOLOR=$(portageq envvar NOCOLOR)
277 [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr)
278 . /etc/init.d/functions.sh
281 # Die if an argument is missing.
282 die_if_missing_arg() {
283 [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1"
286 # Die because an option is not recognized.
287 die_invalid_option() {
288 # Can't use eerror and einfo because this gets called before function.sh
291 echo "Encountered unrecognized option $1." >&2
293 echo "$APP_NAME no longer automatically passes unrecognized options to portage."
294 echo "Separate emerge-only options from revdep-rebuild options with the -- flag."
296 echo "For example, $APP_NAME -v -- --ask"
298 echo "See the man page or $APP_NAME -h for more detail."
303 # Warn about deprecated options.
304 warn_deprecated_opt() {
305 # Can't use eerror and einfo because this gets called before function.sh
308 echo "Encountered deprecated option $1." >&2
309 [[ $2 ]] && echo "Please use $2 instead." >&2
312 # Get whole-word commandline options preceded by two dashes.
315 --nocolor) export NOCOLOR="yes";;
316 --no-color) warn_deprecated_opt "$1" "--nocolor"
317 export NOCOLOR="yes";;
319 --exact) unset PACKAGE_NAMES;;
322 --ignore) RM_OLD_TEMPFILES=1;;
323 --keep-temp) KEEP_TEMP=1;;
324 --library=*) # TODO: check for invalid values
326 unset SEARCH_BROKEN;;
327 --soname=*|--soname-regexp=*) # TODO: check for invalid values
328 warn_deprecated_opt "${1%=*}" "--library"
330 unset SEARCH_BROKEN;;
331 --library) # TODO: check for invalid values
332 die_if_missing_arg $1 $2
335 unset SEARCH_BROKEN;;
336 --soname|--soname-regexp) # TODO: check for invalid values
337 warn_deprecated_opt "$1" "--library"
338 die_if_missing_arg $1 $2
341 unset SEARCH_BROKEN;;
342 --no-ld-path) unset FULL_LD_PATH;;
343 --no-order) unset ORDER_PKGS;;
344 --no-progress) progress() { :; };;
345 --pretend) EMERGE_OPTIONS+=("--pretend");;
346 --quiet) echo_v() { :; }
349 EMERGE_OPTIONS+=($1);;
351 EMERGE_OPTIONS+=("--verbose");;
352 --extra-verbose) warn_deprecated_opt "$1" "--verbose"
354 EMERGE_OPTIONS+=("--verbose");;
355 --package-names) # No longer used, since it is the
356 # default. We accept it for
357 # backwards compatibility.
358 warn_deprecated_opt "$1"
360 *) die_invalid_option $1;;
365 # Get single-letter commandline options preceded by a single dash.
367 local OPT OPTSTRING OPTARG OPTIND
368 while getopts ":CdehikL:loPpqu:vX" OPT; do
370 C) # TODO: Match syntax with the rest of gentoolkit
371 export NOCOLOR="yes";;
373 e) unset PACKAGE_NAMES;;
376 i) RM_OLD_TEMPFILES=1;;
378 L) # TODO: Check for invalid values
379 SONAME="${OPTARG#*=}"
380 unset SEARCH_BROKEN;;
381 l) unset FULL_LD_PATH;;
382 o) unset ORDER_PKGS;;
383 P) progress() { :; };;
384 p) EMERGE_OPTIONS+=("--pretend");;
388 EMERGE_OPTIONS+=("--quiet");;
390 EMERGE_OPTIONS+=("--verbose");;
391 X) # No longer used, since it is the default.
392 # We accept it for backwards compatibility.
393 warn_deprecated_opt "-X"
395 *) die_invalid_option "-$OPTARG";;
400 # Get command-line options.
404 echo_v() { ewarn "$@"; }
405 unset VERBOSE KEEP_TEMP EMERGE_OPTIONS RM_OLD_TEMPFILES
414 EMERGE_OPTIONS+=("$@")
419 [[ ${1:--} = -* ]] && break
421 if [[ ${args[0]} = --* ]]; then
422 get_longopts "${args[@]}"
424 get_shortopts "${args[@]}"
426 *) die_invalid_option "$1";;
432 normalize_emerge_opts
434 # If the user is not super, add --pretend to EMERGE_OPTIONS
435 if [[ ${EMERGE_OPTIONS[@]} != *--pretend* && $UID -ne 0 ]]; then
436 ewarn "You are not superuser. Adding --pretend to emerge options."
437 EMERGE_OPTIONS+=(--pretend)
441 # Is there a --pretend or --fetchonly flag in the EMERGE_OPTIONS array?
443 [[ ${EMERGE_OPTIONS[@]} != *--pretend* &&
444 ${EMERGE_OPTIONS[@]} != *--fetchonly* ]]
447 # Clean up temporary files and exit
450 die 1 " ...terminated. Removing incomplete $@."
455 trap "cleanup_and_die $*" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
459 # Returns 0 if the first arg is found in the remaining args, 1 otherwise
460 # (Returns 2 if given fewer than 2 arguments)
462 (( $# > 1 )) || return 2
463 local IFS=$'\a' target="$1"
465 [[ $'\a'"$*"$'\a' = *$'\a'$target$'\a'* ]]
468 # Dies when it can't change directories
470 if builtin cd -P "$@"; then
471 if [[ $1 != $PWD ]]; then
472 # Some symlink malfeasance is going on
473 die 1 "Working directory expected to be $1, but it is $PWD"
476 die 1 "Unable to change working directory to '$@'"
480 # Tries not to delete any files or directories it shouldn't
483 # Anything in the FILES array in tmpdir is fair game for removal
486 [[ $APP_NAME ]] || die 1 '$APP_NAME is not defined! (This is a bug.)'
488 */*|*-r*|*-R*) die 1 "Oops, I'm not allowed to delete that. ($@)";;
491 # Don't delete files that are not listed in the array
492 # Allow no slashes or recursive deletes at all.
494 */*|-*r*|-*R*) :;; # Not OK
497 has "$i" "${FILES[@]}" && continue
498 die 1 "Oops, I'm not allowed to delete that. ($@)"
502 # delete this setup function so it's harmless to re-run
506 # Make our temporary files directory
507 # $1 - directory name
510 umask 007 || die $? "Unable to set umask 007"
512 die 1 'Temporary file path is unset! (This is a bug.)'
513 elif [[ -d $1 ]]; then
514 # HACK: I hate using find this way
515 if [[ $(find "$1" -type d ! \( -user $2 -perm -0700 \) ) ]]; then
516 eerror "Incorrect permissions on $1"
517 eerror "or at least one file in $1."
518 die 1 "Please make sure it's not a symlink and then remove it."
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" "$uid"
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
741 gawk -F"[=']" '/^dependency_libs/{
742 gsub("^-[^[:space:]]*", "", $3);
743 gsub("[[:space:]]-[^[:space:]]*", "", $3);
747 if [[ $depend = /* && ! -e $depend ]]; then
748 echo "obj $target_file" >> "$BROKEN_FILE"
749 echo_v " broken $target_file (requires $depend)"
754 progress $((++i)) $numFiles $target_file ||
755 progress $((++i)) $numFiles
757 if [[ $SEARCH_BROKEN ]]; then
758 # Look for missing version
759 while read target_file; do
760 echo "obj $target_file" >> "$BROKEN_FILE"
761 echo_v " broken $target_file (no version information available)"
763 # Regexify LD_LIBRARY_MASK. Exclude it from the search.
764 LD_LIBRARY_MASK="${LD_LIBRARY_MASK//$'\n'/|}"
765 gawk -v ldmask="(${LD_LIBRARY_MASK//./\\\.})" '
766 /no version information available/ && $0 !~ ldmask {
767 gsub(/[()]/, "", $NF)
768 if (seen[$NF]++) next
773 [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]] || clean_exit
774 sort -u "$BROKEN_FILE" -o "$BROKEN_FILE"
775 einfo "Generated new $BROKEN_FILE"
783 einfo 'Assigning files to packages'
784 if [[ -r "$RAW_FILE" && -s "$RAW_FILE" ]]; then
785 einfo "Found existing $RAW_FILE"
787 clean_trap "$RAW_FILE" "$OWNERS_FILE"
788 while read obj target_file; do
789 EXACT_PKG=$(get_file_owner $target_file)
790 if [[ $EXACT_PKG ]]; then
791 # Strip version information
792 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
794 echo "$EXACT_PKG" >> "$RAW_FILE"
795 echo "$target_file -> $EXACT_PKG" >> "$OWNERS_FILE"
796 echo_v " $target_file -> $PKG"
798 ewarn " !!! $target_file not owned by any package is broken !!!"
799 echo "$target_file -> (none)" >> "$OWNERS_FILE"
800 echo_v " $target_file -> (none)"
802 done < "$BROKEN_FILE"
803 einfo "Generated new $RAW_FILE and $OWNERS_FILE"
805 # if we find '(none)' on every line, exit out
806 if ! grep -qvF '(none)' "$OWNERS_FILE"; then
807 ewarn "Found some broken files, but none of them were associated with known packages"
808 ewarn "Unable to proceed with automatic repairs."
809 ewarn "The broken files are listed in $OWNERS_FILE"
810 if [[ $VERBOSE ]]; then
811 ewarn "The broken files are:"
812 while read filename junk; do
814 done < "$OWNERS_FILE"
816 exit 0 # FIXME: Should we exit 1 here?
820 einfo 'Cleaning list of packages to rebuild'
821 if [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
822 einfo "Found existing $PKGS_FILE"
824 sort -u "$RAW_FILE" > "$PKGS_FILE"
825 einfo "Generated new $PKGS_FILE"
828 assign_packages_to_ebuilds() {
832 einfo 'Assigning packages to ebuilds'
833 if [[ -r "$EBUILDS_FILE" && -s "$EBUILDS_FILE" ]]; then
834 einfo "Found existing $EBUILDS_FILE"
835 elif [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
836 clean_trap "$EBUILDS_FILE"
837 while read EXACT_PKG; do
839 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
841 SLOT=$(</var/db/pkg/$EXACT_PKG/SLOT)
843 done < "$PKGS_FILE" > "$EBUILDS_FILE"
844 einfo "Generated new $EBUILDS_FILE"
846 einfo 'Nothing to rebuild.'
847 die 1 '(The program should have already quit, so this is a minor bug.)'
850 get_exact_ebuilds() {
851 einfo 'Assigning files to ebuilds'
852 if [[ -r $EBUILDS_FILE && -s $EBUILDS_FILE ]]; then
853 einfo "Found existing $EBUILDS_FILE"
854 elif [[ -r $BROKEN_FILE && -s $BROKEN_FILE ]]; then
855 rebuildList=" $(<"$BROKEN_FILE") "
856 rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ })
857 get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$EBUILDS_FILE"
858 einfo "Generated new $EBUILDS_FILE"
860 einfo 'Nothing to rebuild.'
861 die 1 '(The program should have already quit, so this is a minor bug.)'
864 list_skipped_packages() {
866 ewarn 'Portage could not find any version of the following packages it could build:'
867 ewarn "${SKIP_LIST[@]}"
869 ewarn '(Perhaps they are masked, blocked, or removed from portage.)'
870 ewarn 'Try to emerge them manually.'
874 local -r OLD_EMERGE_DEFAULT_OPTS="$EMERGE_DEFAULT_OPTS"
875 local RAW_REBUILD_LIST
878 if [[ ! $ORDER_PKGS ]]; then
879 einfo 'Skipping package ordering'
882 einfo 'Evaluating package order'
883 if [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]]; then
884 einfo "Found existing $ORDER_FILE"
886 clean_trap "$ORDER_FILE"
887 RAW_REBUILD_LIST=$(<"$EBUILDS_FILE")
888 if [[ $RAW_REBUILD_LIST ]]; then
889 export EMERGE_DEFAULT_OPTS="--nospinner --pretend --oneshot --quiet"
890 RAW_REBUILD_LIST=($RAW_REBUILD_LIST) # convert into array
891 # If PACKAGE_NAMES is defined we're using slots, not versions
892 if [[ $PACKAGE_NAMES ]]; then
893 # Eliminate atoms that can't be built
894 for i in "${!RAW_REBUILD_LIST[@]}"; do
895 if [[ "${RAW_REBUILD_LIST[i]}" = *[A-Za-z]* ]]; then
896 portageq best_visible "$PORTAGE_ROOT" "${RAW_REBUILD_LIST[i]}" >/dev/null && continue
897 SKIP_LIST+=("${RAW_REBUILD_LIST[i]}")
899 unset RAW_REBUILD_LIST[i]
901 # If RAW_REBUILD_LIST is empty, then we have nothing to build.
902 if (( ${#RAW_REBUILD_LIST[@]} == 0 )); then
903 if (( ${#SKIP_LIST[@]} == 0 )); then
904 ewarn "The list of packages to skip is empty, but there are no"
905 ewarn "packages listed to rebuild either. (This is a bug.)"
907 list_skipped_packages
909 die 1 'Warning: Portage cannot rebuild any of the necessary packages.'
912 RAW_REBUILD_LIST="${RAW_REBUILD_LIST[@]}"
913 REBUILD_GREP=$(emerge --nodeps $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g')
914 if (( ${PIPESTATUS[0]} == 0 )); then
915 emerge --deep $RAW_REBUILD_LIST |
916 sed 's/\[[^]]*\]//g' |
917 grep -F "$REBUILD_GREP" > "$ORDER_FILE"
920 # Here we use the PIPESTATUS from the second emerge, the --deep one.
921 if (( ${PIPESTATUS[0]} != 0 )); then
923 eerror 'Warning: Failed to resolve package order.'
924 eerror 'Will merge in arbitrary order'
928 - An ebuild is no longer in the portage tree.
929 - An ebuild is masked, use /etc/portage/packages.keyword
930 and/or /etc/portage/package.unmask to unmask it
935 export EMERGE_DEFAULT_OPTS="$OLD_EMERGE_DEFAULT_OPTS"
937 einfo 'Nothing to rebuild.'
938 die 1 '(The program should have already quit, so this is a minor bug.)'
941 [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]] && einfo "Generated new $ORDER_FILE"
944 show_unowned_files() {
945 if grep -qF '(none)' "$OWNERS_FILE"; then
946 ewarn "Found some broken files that weren't associated with known packages"
947 ewarn "The broken files are:"
948 while read filename junk; do
949 [[ $junk = *none* ]] && ewarn " $filename"
950 done < "$OWNERS_FILE" | gawk '!s[$0]++' # (omit dupes)
954 # Setup portage and the search paths
956 local PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS)
957 PORTAGE_ROOT=$(portageq envvar ROOT)
959 # Obey PORTAGE_NICENESS
960 if [[ $PORTAGE_NICENESS ]]; then
961 renice $PORTAGE_NICENESS $$ > /dev/null
962 # Since we have already set our nice value for our processes,
963 # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
964 export PORTAGE_NICENESS="0"
967 PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
971 # Setup the paths to search (and filter the ones to avoid)
972 setup_search_paths_and_masks() {
973 local configfile sdir mdir skip_me filter_SEARCH_DIRS
975 einfo "Configuring search environment for $APP_NAME"
977 # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
978 # portage, and the environment
980 # Read the incremental variables from environment and portage
981 # Until such time as portage supports these variables as incrementals
982 # The value will be what is in /etc/make.conf
983 SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
984 SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
985 LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
988 if [[ -d /etc/revdep-rebuild ]]; then
989 for configfile in /etc/revdep-rebuild/*; do
990 SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS)
991 SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK)
992 LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK)
995 SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
996 SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
997 LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
1000 # Get the ROOTPATH and PATH from /etc/profile.env
1001 if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
1002 SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
1005 # Get the directories from /etc/ld.so.conf
1006 if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
1007 SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
1010 # Set the final variables
1011 SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
1012 SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
1013 LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
1014 # Filter masked paths from SEARCH_DIRS
1015 for sdir in ${SEARCH_DIRS} ; do
1017 for mdir in ${SEARCH_DIRS_MASK}; do
1018 [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break
1020 [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
1022 SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}")
1023 [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
1026 # Rebuild packages owning broken binaries
1028 if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
1029 REBUILD_LIST=( $(<"$LIST.5_order") )
1030 REBUILD_LIST="${REBUILD_LIST[@]/#/=}"
1032 REBUILD_LIST=$(sort -u "$EBUILDS_FILE")
1035 trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1037 einfo 'All prepared. Starting rebuild'
1038 echo "emerge --oneshot ${EMERGE_OPTIONS[@]} $REBUILD_LIST"
1040 is_real_merge && countdown 10
1042 # Link file descriptor #6 with stdin so --ask will work
1045 # Run in background to correctly handle Ctrl-C
1047 EMERGE_DEFAULT_OPTS="--oneshot ${EMERGE_OPTIONS[@]}" emerge $REBUILD_LIST <&6
1048 echo $? > "$STATUS_FILE"
1052 # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
1058 if (( $(<"$STATUS_FILE") != 0 )); then
1060 ewarn "$APP_NAME failed to emerge all packages."
1061 ewarn 'you have the following choices:'
1062 einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
1063 einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
1064 einfo " (and remove $ORDER_FILE to be evaluated again)"
1065 einfo '- Modify the above emerge command and run it manually.'
1066 einfo '- Compile or unmerge unsatisfied packages manually,'
1067 einfo ' remove temporary files, and try again.'
1068 einfo ' (you can edit package/ebuild list first)'
1070 einfo 'To remove temporary files, please run:'
1071 einfo "rm ${WORKING_DIR}/*.rr"
1074 elif is_real_merge; then
1076 eerror "terminated. Please remove the temporary files manually:"
1077 eerror "rm ${WORKING_DIR}/*.rr"
1080 [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages
1081 trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1082 einfo 'Build finished correctly. Removing temporary files...'
1084 einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
1085 einfo 'are fixed. Possible reasons for remaining inconsistencies include:'
1086 einfo ' orphaned files'
1087 einfo ' deep dependencies'
1088 einfo " packages installed outside of portage's control"
1089 einfo ' specially-evaluated libraries'
1090 if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then
1093 [[ $KEEP_TEMP ]] || rm "${FILES[@]}"
1095 einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'