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.
255 # NOTE: depends on app-misc/realpath!
258 # Add a space to the end of each object name to prevent false
259 # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
260 # The same for "${rpath} ".
262 rpath=$(realpath "${*}" 2>/dev/null)
263 # To ensure we always have something in rpath...
264 [[ -z $rpath ]] && rpath=${*}
266 find -L /var/db/pkg -name CONTENTS -print0 |
267 xargs -0 grep -Fl -e "${*} " -e "${rpath} " |
268 sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
271 # Normalize some EMERGE_OPTIONS
272 normalize_emerge_opts() {
273 # Normalize some EMERGE_OPTIONS
274 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
275 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
276 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-v/--verbose})
279 # Use the color preference from portage
281 # This should still work if NOCOLOR is set by the -C flag or in the user's
283 export NOCOLOR=$(portageq envvar NOCOLOR)
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 # Setup portage and the search paths
977 local PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS)
978 PORTAGE_ROOT=$(portageq envvar ROOT)
980 # Obey PORTAGE_NICENESS
981 if [[ $PORTAGE_NICENESS ]]; then
982 renice $PORTAGE_NICENESS $$ > /dev/null
983 # Since we have already set our nice value for our processes,
984 # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
985 export PORTAGE_NICENESS="0"
988 PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
992 # Setup the paths to search (and filter the ones to avoid)
993 setup_search_paths_and_masks() {
994 local configfile sdir mdir skip_me filter_SEARCH_DIRS
996 einfo "Configuring search environment for $APP_NAME"
998 # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
999 # portage, and the environment
1001 # Read the incremental variables from environment and portage
1002 # Until such time as portage supports these variables as incrementals
1003 # The value will be what is in /etc/make.conf
1004 SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
1005 SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
1006 LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
1009 if [[ -d /etc/revdep-rebuild ]]; then
1010 for configfile in /etc/revdep-rebuild/*; do
1011 SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS)
1012 SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK)
1013 LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK)
1016 SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
1017 SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
1018 LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
1021 # Get the ROOTPATH and PATH from /etc/profile.env
1022 if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
1023 SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
1026 # Get the directories from /etc/ld.so.conf
1027 if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
1028 SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
1031 # Set the final variables
1032 SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
1033 SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
1034 LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
1035 # Filter masked paths from SEARCH_DIRS
1036 for sdir in ${SEARCH_DIRS} ; do
1038 for mdir in ${SEARCH_DIRS_MASK}; do
1039 [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break
1041 [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
1043 SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}")
1044 [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
1047 # Rebuild packages owning broken binaries
1049 if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
1050 REBUILD_LIST=( $(<"$LIST.5_order") )
1051 REBUILD_LIST="${REBUILD_LIST[@]/#/=}"
1053 REBUILD_LIST=$(sort -u "$EBUILDS_FILE")
1056 trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1058 einfo 'All prepared. Starting rebuild'
1059 echo "emerge --oneshot ${EMERGE_OPTIONS[@]} $REBUILD_LIST"
1061 is_real_merge && countdown 10
1063 # Link file descriptor #6 with stdin so --ask will work
1066 # Run in background to correctly handle Ctrl-C
1068 EMERGE_DEFAULT_OPTS="--oneshot ${EMERGE_OPTIONS[@]}" emerge $REBUILD_LIST <&6
1069 echo $? > "$STATUS_FILE"
1073 # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
1079 if (( $(<"$STATUS_FILE") != 0 )); then
1081 ewarn "$APP_NAME failed to emerge all packages."
1082 ewarn 'you have the following choices:'
1083 einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
1084 einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
1085 einfo " (and remove $ORDER_FILE to be evaluated again)"
1086 einfo '- Modify the above emerge command and run it manually.'
1087 einfo '- Compile or unmerge unsatisfied packages manually,'
1088 einfo ' remove temporary files, and try again.'
1089 einfo ' (you can edit package/ebuild list first)'
1091 einfo 'To remove temporary files, please run:'
1092 einfo "rm ${WORKING_DIR}/*.rr"
1095 elif is_real_merge; then
1097 eerror "terminated. Please remove the temporary files manually:"
1098 eerror "rm ${WORKING_DIR}/*.rr"
1101 [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages
1102 trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1103 einfo 'Build finished correctly. Removing temporary files...'
1105 einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
1106 einfo 'are fixed. Possible reasons for remaining inconsistencies include:'
1107 einfo ' orphaned files'
1108 einfo ' deep dependencies'
1109 einfo " packages installed outside of portage's control"
1110 einfo ' specially-evaluated libraries'
1111 if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then
1114 [[ $KEEP_TEMP ]] || rm "${FILES[@]}"
1116 einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'