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
93 setup_search_paths_and_masks
97 # Search for broken binaries
102 # Associate broken binaries with packages to rebuild
103 if [[ $PACKAGE_NAMES ]]; then
106 assign_packages_to_ebuilds
111 # Rebuild packages owning broken binaries
119 # Refuse to delete anything before we cd to our tmpdir
120 # (See mkdir_and_cd_to_tmpdir()
122 eerror "I was instructed to rm '$@'"
123 die 1 "Refusing to delete anything before changing to temporary directory."
126 # GNU find has -executable, but if our users' finds do not have that flag
127 # we emulate it with this function. Also emulates -writable and -readable.
128 # Usage: find PATH ARGS -- use find like normal, except use -executable instead
129 # of various versions of -perm /+ blah blah and hacks
131 hash find || { die 1 'find not found!'; }
132 # We can be pretty sure find itself should be executable.
133 local testsubject="$(type -P find)"
134 if [[ $(command find "$testsubject" -executable 2> /dev/null) ]]; then
135 unset -f find # We can just use the command find
136 elif [[ $(command find "$testsubject" -perm /u+x 2> /dev/null) ]]; then
138 a=(${@//-executable/-perm \/u+x})
139 a=(${a[@]//-writable/-perm \/u+w})
140 a=(${a[@]//-readable/-perm \/r+w})
141 command find "${a[@]}"
143 elif [[ $(command find "$testsubject" -perm +u+x 2> /dev/null) ]]; then
145 a=(${@//-executable/-perm +u+x})
146 a=(${a[@]//-writable/-perm +u+w})
147 a=(${a[@]//-readable/-perm +r+w})
148 command find "${a[@]}"
152 a=(${@//-executable/-exec test -x '{}' \; -print})
153 a=(${a[@]//-writable/-exec test -w '{}' \; -print})
154 a=(${a[@]//-readable/-exec test -r '{}' \; -print})
155 command find "${a[@]}"
163 Usage: $APP_NAME [OPTIONS] [--] [EMERGE_OPTIONS]
165 Broken reverse dependency rebuilder.
167 -C, --nocolor Turn off colored output
168 -d, --debug Print way too much information (uses bash's set -xv)
169 -e, --exact Emerge based on exact package version
170 -h, --help Print this usage
171 -i, --ignore Ignore temporary files from previous runs
172 -k, --keep-temp Do not delete temporary files on exit
173 -L, --library NAME Emerge existing packages that use the library with NAME
174 --library=NAME NAME can be a full path to the library or a basic
175 regular expression (man grep)
176 -l, --no-ld-path Do not set LD_LIBRARY_PATH
177 -o, --no-order Do not check the build order
178 (Saves time, but may cause breakage.)
179 -p, --pretend Do a trial run without actually emerging anything
180 (also passed to emerge command)
181 -P, --no-progress Turn off the progress meter
182 -q, --quiet Be less verbose (also passed to emerge command)
183 -v, --verbose Be more verbose (also passed to emerge command)
185 Calls emerge, options after -- are ignored by $APP_NAME
186 and passed directly to emerge.
188 Report bugs to <http://bugs.gentoo.org>
192 # Usage: progress i n
194 # n: total number of items to process
198 local curProg=$(( $1 * 100 / $2 ))
199 (( curProg == OLDPROG )) && return # no change, output nothing
200 OLDPROG="$curProg" # must be a global variable
201 (( $1 == $2 )) && local lb=$'\n'
202 echo -ne '\r \r'"[ $curProg% ] $lb"
205 else # STDOUT is not a tty. Disable progress meter.
211 # n: number of seconds to count
214 for ((i=1; i<$1; i++)); do
221 # Replace whitespace with linebreaks, normalize repeated '/' chars, and sort -u
222 # (If any libs have whitespace in their filenames, someone needs punishment.)
224 awk 'BEGIN {RS="[[:space:]]"}
226 /[^[:space:]]/ {gsub(/\/\/+/, "/"); print}' | sort -u
229 # Exit and optionally output to sterr
237 # What to do when dynamic linking is consistent
239 [[ $KEEP_TEMP ]] || rm -f "${FILES[@]}"
241 einfo "$OK_TEXT... All done. "
245 # Get the name of the package that owns a file or list of files given as args.
248 # ${*/%/ } adds a space to the end of each object name to prevent false
249 # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
250 find -L /var/db/pkg -name CONTENTS -print0 |
251 xargs -0 grep -Fl "${*/%/ }" |
252 sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
255 # Normalize some EMERGE_OPTIONS
256 normalize_emerge_opts() {
257 # Normalize some EMERGE_OPTIONS
258 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
259 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
260 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--verbose})
263 # Use the color preference from portage
265 # This should still work if NOCOLOR is set by the -C flag or in the user's
267 export NOCOLOR=$(portageq envvar NOCOLOR)
268 [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr)
269 . /etc/init.d/functions.sh
272 # Die if an argument is missing.
273 die_if_missing_arg() {
274 [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1"
277 # Die because an option is not recognized.
278 die_invalid_option() {
279 # Can't use eerror and einfo because this gets called before function.sh
282 echo "Encountered unrecognized option $1." >&2
284 echo "$APP_NAME no longer automatically passes unrecognized options to portage."
285 echo "Separate emerge-only options from revdep-rebuild options with the -- flag."
287 echo "For example, $APP_NAME -v -- --ask"
289 echo "See the man page or $APP_NAME -h for more detail."
294 # Warn about deprecated options.
295 warn_deprecated_opt() {
296 # Can't use eerror and einfo because this gets called before function.sh
299 echo "Encountered deprecated option $1." >&2
300 [[ $2 ]] && echo "Please use $2 instead." >&2
303 # Get whole-word commandline options preceded by two dashes.
306 --nocolor) export NOCOLOR="yes";;
307 --no-color) warn_deprecated_opt "$1" "--nocolor"
308 export NOCOLOR="yes";;
310 --exact) unset PACKAGE_NAMES;;
313 --ignore) RM_OLD_TEMPFILES=1;;
314 --keep-temp) KEEP_TEMP=1;;
315 --library=*) # TODO: check for invalid values
317 unset SEARCH_BROKEN;;
318 --soname=*|--soname-regexp=*) # TODO: check for invalid values
319 warn_deprecated_opt "${1%=*}" "--library"
321 unset SEARCH_BROKEN;;
322 --library) # TODO: check for invalid values
323 die_if_missing_arg $1 $2
326 unset SEARCH_BROKEN;;
327 --soname|--soname-regexp) # TODO: check for invalid values
328 warn_deprecated_opt "$1" "--library"
329 die_if_missing_arg $1 $2
332 unset SEARCH_BROKEN;;
333 --no-ld-path) unset FULL_LD_PATH;;
334 --no-order) unset ORDER_PKGS;;
335 --no-progress) progress() { :; };;
336 --pretend) EMERGE_OPTIONS+=("--pretend");;
337 --quiet) echo_v() { :; }
340 EMERGE_OPTIONS+=($1);;
342 EMERGE_OPTIONS+=("--verbose");;
343 --extra-verbose) warn_deprecated_opt "$1" "--verbose"
345 EMERGE_OPTIONS+=("--verbose");;
346 --package-names) # No longer used, since it is the
347 # default. We accept it for
348 # backwards compatibility.
349 warn_deprecated_opt "$1"
351 *) die_invalid_option $1;;
356 # Get single-letter commandline options preceded by a single dash.
358 local OPT OPTSTRING OPTARG OPTIND
359 while getopts ":CdehikL:loPpqu:vX" OPT; do
361 C) # TODO: Match syntax with the rest of gentoolkit
362 export NOCOLOR="yes";;
364 e) unset PACKAGE_NAMES;;
367 i) RM_OLD_TEMPFILES=1;;
369 L) # TODO: Check for invalid values
370 SONAME="${OPTARG#*=}"
371 unset SEARCH_BROKEN;;
372 l) unset FULL_LD_PATH;;
373 o) unset ORDER_PKGS;;
374 P) progress() { :; };;
375 p) EMERGE_OPTIONS+=("--pretend");;
379 EMERGE_OPTIONS+=("--quiet");;
381 EMERGE_OPTIONS+=("--verbose");;
382 X) # No longer used, since it is the default.
383 # We accept it for backwards compatibility.
384 warn_deprecated_opt "-X"
386 *) die_invalid_option "-$OPTARG";;
391 # Get command-line options.
395 echo_v() { ewarn "$@"; }
396 unset VERBOSE KEEP_TEMP EMERGE_OPTIONS RM_OLD_TEMPFILES
405 EMERGE_OPTIONS+=("$@")
410 [[ ${1:--} = -* ]] && break
412 if [[ ${args[0]} = --* ]]; then
413 get_longopts "${args[@]}"
415 get_shortopts "${args[@]}"
417 *) die_invalid_option "$1";;
423 normalize_emerge_opts
425 # If the user is not super, add --pretend to EMERGE_OPTIONS
426 if [[ ${EMERGE_OPTIONS[@]} != *--pretend* && $UID -ne 0 ]]; then
427 ewarn "You are not superuser. Adding --pretend to emerge options."
428 EMERGE_OPTIONS+=(--pretend)
432 # Is there a --pretend or --fetchonly flag in the EMERGE_OPTIONS array?
434 [[ ${EMERGE_OPTIONS[@]} != *--pretend* &&
435 ${EMERGE_OPTIONS[@]} != *--fetchonly* ]]
438 # Clean up temporary files and exit
441 die 1 " ...terminated. Removing incomplete $@."
446 trap "cleanup_and_die $*" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
450 # Returns 0 if the first arg is found in the remaining args, 1 otherwise
451 # (Returns 2 if given fewer than 2 arguments)
453 (( $# > 1 )) || return 2
454 local IFS=$'\a' target="$1"
456 [[ $'\a'"$*"$'\a' = *$'\a'$target$'\a'* ]]
459 # Dies when it can't change directories
461 if builtin cd -P "$@"; then
462 if [[ $1 != $PWD ]]; then
463 # Some symlink malfeasance is going on
464 die 1 "Working directory expected to be $1, but it is $PWD"
467 die 1 "Unable to change working directory to '$@'"
471 # Tries not to delete any files or directories it shouldn't
474 # Anything in the FILES array in tmpdir is fair game for removal
477 [[ $APP_NAME ]] || die 1 '$APP_NAME is not defined! (This is a bug.)'
479 */*|*-r*|*-R*) die 1 "Oops, I'm not allowed to delete that. ($@)";;
482 # Don't delete files that are not listed in the array
483 # Allow no slashes or recursive deletes at all.
485 */*|-*r*|-*R*) :;; # Not OK
488 has "$i" "${FILES[@]}" && continue
489 die 1 "Oops, I'm not allowed to delete that. ($@)"
493 # delete this setup function so it's harmless to re-run
497 # Make our temporary files directory
499 umask 007 || die $? "Unable to set umask 007"
501 die 1 'Temporary file path is unset! (This is a bug.)'
502 elif [[ -d $1 ]]; then
503 # HACK: I hate using find this way
504 if [[ $(find "$1" -type d ! \( -user $2 -group portage -perm -0700 \) ) ]]; then
505 eerror "Incorrect permissions on $1"
506 eerror "or at least one file in $1."
507 die 1 "Please make sure it's not a symlink and then remove it."
510 elif mkdir -m 0700 "$1" && chown :portage "$1"; then
513 die 1 "Unable to find or create a satisfactory location for temporary files"
515 [[ $VERBOSE ]] && einfo "Temporary cache files are located in $PWD"
521 local uid=$(python -c 'import os; import pwd; print pwd.getpwuid(os.getuid())[0]')
522 # Find a place to put temporary files
523 # Use "${TMPDIR}/revdep-rebuild" or /tmp/revdep-rebuild
524 local tmp_target="/var/tmp/${APP_NAME}-${uid}-cache"
526 # From here on all work is done inside the temporary directory
527 setup_tmpdir "$tmp_target" "$uid"
529 if [[ $SEARCH_BROKEN ]]; then
530 SONAME_SEARCH="$SONAME"
531 HEAD_TEXT="broken by a package update"
532 OK_TEXT="Dynamic linking on your system is consistent"
533 WORKING_TEXT="consistency"
535 # first case is needed to test against /path/to/foo.so
536 if [[ $SONAME = /* ]]; then
537 # Set to "<space>$SONAME<space>"
538 SONAME_SEARCH=" $SONAME "
539 # Escape the "/" characters
540 SONAME_SEARCH="${SONAME_SEARCH//\//\\/}"
542 # Set to "<tab>$SONAME<space>"
543 SONAME_SEARCH=$'\t'"$SONAME "
545 # Hash the SONAME, to avoid unprintable characters. We are using HMAC since
546 # the interface has remained consistent since python 2.2
547 local uuid_hash=$(python -c "import hmac; print hmac.new('revdep-rebuild',\"${SONAME##*/}\").hexdigest()")
548 # Delete me - keeping for reference if needed
549 #local uuid="${SONAME##*/}"
550 #uuid="${uuid//[[:space:]]}"
553 HEAD_TEXT="using $SONAME"
554 OK_TEXT="There are no dynamic links to $SONAME"
556 setup_tmpdir "$tmp_target"/"$uuid_hash" "$uid"
559 # If any of our temporary files are older than 1 day, remove them all
560 if [[ ! $KEEP_TEMP ]]; then
564 done < <(find -L . -maxdepth 1 -type f -name '*.rr' -mmin +1440 -print 2>/dev/null)
567 # Compare old and new environments
568 # Don't use our previous files if environment doesn't match
570 # We do not care if these emerge options change
571 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--pretend/})
572 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--fetchonly/})
573 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--verbose/})
575 SEARCH_DIRS="$SEARCH_DIRS"
576 SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK"
577 LD_LIBRARY_MASK="$LD_LIBRARY_MASK"
578 PORTAGE_ROOT="$PORTAGE_ROOT"
579 EMERGE_OPTIONS="${EMERGE_OPTIONS[@]}"
580 ORDER_PKGS="$ORDER_PKGS"
581 FULL_LD_PATH="$FULL_LD_PATH"
584 if [[ -r "$ENV_FILE" && -s "$ENV_FILE" ]]; then
585 old_env=$(<"$ENV_FILE")
586 if [[ $old_env != $new_env ]]; then
587 ewarn 'Environment mismatch from previous run, deleting temporary files...'
591 # No env file found, silently delete any other tempfiles that may exist
595 # If we should remove old tempfiles, do so
596 if [[ $RM_OLD_TEMPFILES ]]; then
599 for file in "${FILES[@]}"; do
600 if [ -e "$file" ]; then
601 chown ${uid}:portage "$file"
607 # Save the environment in a file for next time
608 echo "$new_env" > "$ENV_FILE"
610 [[ $VERBOSE ]] && echo $'\n'"$APP_NAME environment:"$'\n'"$new_env"
613 einfo "Checking reverse dependencies"
614 einfo "Packages containing binaries and libraries $HEAD_TEXT"
615 einfo "will be emerged."
618 einfo "Collecting system binaries and libraries"
619 if [[ -r "$FILES_FILE" && -s "$FILES_FILE" ]]; then
620 einfo "Found existing $FILES_FILE"
622 # Be safe and remove any extraneous temporary files
623 # Don't remove 0_env.rr - The first file in the array
624 rm -f "${FILES[@]:1}"
626 clean_trap "$FILES_FILE"
628 if [[ $SEARCH_DIRS_MASK ]]; then
629 findMask=($SEARCH_DIRS_MASK)
630 findMask="${findMask[@]/#/-o -path }"
631 findMask="( ${findMask#-o } ) -prune -o"
634 find ${SEARCH_DIRS[@]} $findMask -type f \( -executable -o \
635 -name '*.so' -o -name '*.so.*' -o -name '*.la' \) -print 2> /dev/null |
636 sort -u > "$FILES_FILE" ||
637 die $? "find failed to list binary files (This is a bug.)"
638 einfo "Generated new $FILES_FILE"
642 local COMPLETE_LD_LIBRARY_PATH
643 [[ $SEARCH_BROKEN && $FULL_LD_PATH ]] || return
644 einfo 'Collecting complete LD_LIBRARY_PATH'
645 if [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]]; then
646 einfo "Found existing $LDPATH_FILE."
648 clean_trap "$LDPATH_FILE"
649 # Ensure that the "trusted" lib directories are at the start of the path
650 COMPLETE_LD_LIBRARY_PATH=(
653 $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf)
654 $(sed 's:/[^/]*$::' < "$FILES_FILE" | sort -ru)
657 COMPLETE_LD_LIBRARY_PATH="${COMPLETE_LD_LIBRARY_PATH[*]}"
659 echo "$COMPLETE_LD_LIBRARY_PATH" > "$LDPATH_FILE"
660 einfo "Generated new $LDPATH_FILE"
670 local COMPLETE_LD_LIBRARY_PATH
671 if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
672 [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]] ||
673 die 1 "Unable to find $LDPATH_FILE"
674 COMPLETE_LD_LIBRARY_PATH=$(<"$LDPATH_FILE")
676 einfo "Checking dynamic linking $WORKING_TEXT"
677 if [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]]; then
678 einfo "Found existing $BROKEN_FILE."
680 clean_trap "$BROKEN_FILE" "$ERRORS_FILE"
681 files=($(<"$FILES_FILE"))
682 numFiles="${#files[@]}"
683 for target_file in "${files[@]}"; do
684 if [[ $target_file != *.la ]]; then
685 # Note: double checking seems to be faster than single with complete path
686 # (special add ons are rare).
687 ldd_output=$(ldd "$target_file" 2>> "$ERRORS_FILE" | sort -u)
688 ldd_status=$? # TODO: Check this for problems with sort
689 # HACK: if LD_LIBRARY_MASK is null or undefined grep -vF doesn't work
690 if grep -vF "${LD_LIBRARY_MASK:=$'\a'}" <<< "$ldd_output" |
691 grep -q "$SONAME_SEARCH"; then
692 if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
693 if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$target_file" 2>/dev/null |
694 grep -vF "$LD_LIBRARY_MASK" | grep -q "$SONAME_SEARCH"; then
695 # FIXME: I hate duplicating code
696 # Only build missing direct dependencies
698 expr='s/[[:space:]]*\([^[:space:]]*\) => not found/\1/p'
699 sed -n "$expr" <<< "$ldd_output"
702 expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
703 objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
705 MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
706 if [[ $MISSING_LIBS ]]; then
707 echo "obj $target_file" >> "$BROKEN_FILE"
708 echo_v " broken $target_file (requires $MISSING_LIBS)"
712 # FIXME: I hate duplicating code
713 # Only rebuild for direct dependencies
715 expr="/$SONAME_SEARCH/s/^[[:space:]]*\([^[:space:]]*\).*$/\1/p"
716 sort -u <<< "$ldd_output" | sed -n "$expr"
719 expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
720 objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
722 MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
723 if [[ $MISSING_LIBS ]]; then
724 echo "obj $target_file" >> "$BROKEN_FILE"
725 if [[ $SEARCH_BROKEN ]]; then
726 echo_v " broken $target_file (requires $MISSING_LIBS)"
728 echo_v " found $target_file"
733 elif [[ $SEARCH_BROKEN ]]; then
734 # Look for broken .la files
736 awk -F"[=']" '/^dependency_libs/{
737 gsub("^-[^[:space:]]*", "", $3);
738 gsub("[[:space:]]-[^[:space:]]*", "", $3);
742 if [[ $depend = /* && ! -e $depend ]]; then
743 echo "obj $target_file" >> "$BROKEN_FILE"
744 echo_v " broken $target_file (requires $depend)"
749 progress $((++i)) $numFiles $target_file ||
750 progress $((++i)) $numFiles
752 if [[ $SEARCH_BROKEN ]]; then
753 # Look for missing version
754 while read target_file; do
755 echo "obj $target_file" >> "$BROKEN_FILE"
756 echo_v " broken $target_file (no version information available)"
758 # Regexify LD_LIBRARY_MASK. Exclude it from the search.
759 LD_LIBRARY_MASK="${LD_LIBRARY_MASK//$'\n'/|}"
760 awk -v ldmask="(${LD_LIBRARY_MASK//./\\\.})" '
761 /no version information available/ && $0 !~ ldmask {
762 gsub(/[()]/, "", $NF)
763 if (seen[$NF]++) next
768 [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]] || clean_exit
769 sort -u "$BROKEN_FILE" -o "$BROKEN_FILE"
770 einfo "Generated new $BROKEN_FILE"
778 einfo 'Assigning files to packages'
779 if [[ -r "$RAW_FILE" && -s "$RAW_FILE" ]]; then
780 einfo "Found existing $RAW_FILE"
782 clean_trap "$RAW_FILE" "$OWNERS_FILE"
783 while read obj target_file; do
784 EXACT_PKG=$(get_file_owner $target_file)
785 if [[ $EXACT_PKG ]]; then
786 # Strip version information
787 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
789 echo "$EXACT_PKG" >> "$RAW_FILE"
790 echo "$target_file -> $EXACT_PKG" >> "$OWNERS_FILE"
791 echo_v " $target_file -> $PKG"
793 ewarn " !!! $target_file not owned by any package is broken !!!"
794 echo "$target_file -> (none)" >> "$OWNERS_FILE"
795 echo_v " $target_file -> (none)"
797 done < "$BROKEN_FILE"
798 einfo "Generated new $RAW_FILE and $OWNERS_FILE"
800 # if we find '(none)' on every line, exit out
801 if ! grep -qvF '(none)' "$OWNERS_FILE"; then
802 ewarn "Found some broken files, but none of them were associated with known packages"
803 ewarn "Unable to proceed with automatic repairs."
804 ewarn "The broken files are listed in $OWNERS_FILE"
805 if [[ $VERBOSE ]]; then
806 ewarn "The broken files are:"
807 while read filename junk; do
809 done < "$OWNERS_FILE"
811 exit 0 # FIXME: Should we exit 1 here?
815 einfo 'Cleaning list of packages to rebuild'
816 if [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
817 einfo "Found existing $PKGS_FILE"
819 sort -u "$RAW_FILE" > "$PKGS_FILE"
820 einfo "Generated new $PKGS_FILE"
823 assign_packages_to_ebuilds() {
827 einfo 'Assigning packages to ebuilds'
828 if [[ -r "$EBUILDS_FILE" && -s "$EBUILDS_FILE" ]]; then
829 einfo "Found existing $EBUILDS_FILE"
830 elif [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
831 clean_trap "$EBUILDS_FILE"
832 while read EXACT_PKG; do
834 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
836 SLOT=$(</var/db/pkg/$EXACT_PKG/SLOT)
838 done < "$PKGS_FILE" > "$EBUILDS_FILE"
839 einfo "Generated new $EBUILDS_FILE"
841 einfo 'Nothing to rebuild.'
842 die 1 '(The program should have already quit, so this is a minor bug.)'
845 get_exact_ebuilds() {
846 einfo 'Assigning files to ebuilds'
847 if [[ -r $EBUILDS_FILE && -s $EBUILDS_FILE ]]; then
848 einfo "Found existing $EBUILDS_FILE"
849 elif [[ -r $BROKEN_FILE && -s $BROKEN_FILE ]]; then
850 rebuildList=" $(<"$BROKEN_FILE") "
851 rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ })
852 get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$EBUILDS_FILE"
853 einfo "Generated new $EBUILDS_FILE"
855 einfo 'Nothing to rebuild.'
856 die 1 '(The program should have already quit, so this is a minor bug.)'
859 list_skipped_packages() {
861 ewarn 'Portage could not find any version of the following packages it could build:'
862 ewarn "${SKIP_LIST[@]}"
864 ewarn '(Perhaps they are masked, blocked, or removed from portage.)'
865 ewarn 'Try to emerge them manually.'
869 local -r OLD_EMERGE_DEFAULT_OPTS="$EMERGE_DEFAULT_OPTS"
870 local RAW_REBUILD_LIST
873 if [[ ! $ORDER_PKGS ]]; then
874 einfo 'Skipping package ordering'
877 einfo 'Evaluating package order'
878 if [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]]; then
879 einfo "Found existing $ORDER_FILE"
881 clean_trap "$ORDER_FILE"
882 RAW_REBUILD_LIST=$(<"$EBUILDS_FILE")
883 if [[ $RAW_REBUILD_LIST ]]; then
884 export EMERGE_DEFAULT_OPTS="--nospinner --pretend --oneshot --quiet"
885 RAW_REBUILD_LIST=($RAW_REBUILD_LIST) # convert into array
886 # If PACKAGE_NAMES is defined we're using slots, not versions
887 if [[ $PACKAGE_NAMES ]]; then
888 # Eliminate atoms that can't be built
889 for i in "${!RAW_REBUILD_LIST[@]}"; do
890 if [[ "${RAW_REBUILD_LIST[i]}" = *[A-Za-z]* ]]; then
891 portageq best_visible "$PORTAGE_ROOT" "${RAW_REBUILD_LIST[i]}" >/dev/null && continue
892 SKIP_LIST+=("${RAW_REBUILD_LIST[i]}")
894 unset RAW_REBUILD_LIST[i]
896 # If RAW_REBUILD_LIST is empty, then we have nothing to build.
897 if (( ${#RAW_REBUILD_LIST[@]} == 0 )); then
898 if (( ${#SKIP_LIST[@]} == 0 )); then
899 ewarn "The list of packages to skip is empty, but there are no"
900 ewarn "packages listed to rebuild either. (This is a bug.)"
902 list_skipped_packages
904 die 1 'Warning: Portage cannot rebuild any of the necessary packages.'
907 RAW_REBUILD_LIST="${RAW_REBUILD_LIST[@]}"
908 REBUILD_GREP=$(emerge --nodeps $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g')
909 if (( ${PIPESTATUS[0]} == 0 )); then
910 emerge --deep $RAW_REBUILD_LIST |
911 sed 's/\[[^]]*\]//g' |
912 grep -F "$REBUILD_GREP" > "$ORDER_FILE"
915 # Here we use the PIPESTATUS from the second emerge, the --deep one.
916 if (( ${PIPESTATUS[0]} != 0 )); then
918 eerror 'Warning: Failed to resolve package order.'
919 eerror 'Will merge in arbitrary order'
923 - An ebuild is no longer in the portage tree.
924 - An ebuild is masked, use /etc/portage/packages.keyword
925 and/or /etc/portage/package.unmask to unmask it
930 export EMERGE_DEFAULT_OPTS="$OLD_EMERGE_DEFAULT_OPTS"
932 einfo 'Nothing to rebuild.'
933 die 1 '(The program should have already quit, so this is a minor bug.)'
936 [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]] && einfo "Generated new $ORDER_FILE"
939 show_unowned_files() {
940 if grep -qF '(none)' "$OWNERS_FILE"; then
941 ewarn "Found some broken files that weren't associated with known packages"
942 ewarn "The broken files are:"
943 while read filename junk; do
944 [[ $junk = *none* ]] && ewarn " $filename"
945 done < "$OWNERS_FILE" | awk '!s[$0]++' # (omit dupes)
949 # Setup portage and the search paths
951 local PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS)
952 PORTAGE_ROOT=$(portageq envvar ROOT)
954 # Obey PORTAGE_NICENESS
955 if [[ $PORTAGE_NICENESS ]]; then
956 renice $PORTAGE_NICENESS $$ > /dev/null
957 # Since we have already set our nice value for our processes,
958 # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
959 export PORTAGE_NICENESS="0"
962 PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
966 # Setup the paths to search (and filter the ones to avoid)
967 setup_search_paths_and_masks() {
968 local configfile sdir mdir skip_me filter_SEARCH_DIRS
970 einfo "Configuring search environment for $APP_NAME"
972 # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
973 # portage, and the environment
975 # Read the incremental variables from environment and portage
976 # Until such time as portage supports these variables as incrementals
977 # The value will be what is in /etc/make.conf
978 SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
979 SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
980 LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
983 if [[ -d /etc/revdep-rebuild ]]; then
984 for configfile in /etc/revdep-rebuild/*; do
985 SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS)
986 SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK)
987 LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK)
990 SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
991 SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
992 LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
995 # Get the ROOTPATH and PATH from /etc/profile.env
996 if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
997 SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
1000 # Get the directories from /etc/ld.so.conf
1001 if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
1002 SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
1005 # Set the final variables
1006 SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
1007 SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
1008 LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
1009 # Filter masked paths from SEARCH_DIRS
1010 for sdir in ${SEARCH_DIRS} ; do
1011 for mdir in ${SEARCH_DIRS_MASK}; do
1012 [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break
1014 [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
1017 SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}")
1018 [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
1021 # Rebuild packages owning broken binaries
1023 if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
1024 REBUILD_LIST=( $(<"$LIST.5_order") )
1025 REBUILD_LIST="${REBUILD_LIST[@]/#/=}"
1027 REBUILD_LIST=$(sort -u "$EBUILDS_FILE")
1030 trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1032 einfo 'All prepared. Starting rebuild'
1033 echo "emerge --oneshot ${EMERGE_OPTIONS[@]} $REBUILD_LIST"
1035 is_real_merge && countdown 10
1037 # Link file descriptor #6 with stdin so --ask will work
1040 # Run in background to correctly handle Ctrl-C
1042 EMERGE_DEFAULT_OPTS="--oneshot ${EMERGE_OPTIONS[@]}" emerge $REBUILD_LIST <&6
1043 echo $? > "$STATUS_FILE"
1047 # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
1053 if (( $(<"$STATUS_FILE") != 0 )); then
1055 ewarn "$APP_NAME failed to emerge all packages."
1056 ewarn 'you have the following choices:'
1057 einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
1058 einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
1059 einfo " (and remove $ORDER_FILE to be evaluated again)"
1060 einfo '- Modify the above emerge command and run it manually.'
1061 einfo '- Compile or unmerge unsatisfied packages manually,'
1062 einfo ' remove temporary files, and try again.'
1063 einfo ' (you can edit package/ebuild list first)'
1065 einfo 'To remove temporary files, please run:'
1066 einfo "rm ${TMPDIR}/$APP_NAME/*.rr"
1069 elif is_real_merge; then
1071 eerror "terminated. Please remove the temporary files manually:"
1072 eerror "rm ${TMPDIR}/$APP_NAME/*.rr"
1075 [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages
1076 trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1077 einfo 'Build finished correctly. Removing temporary files...'
1079 einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
1080 einfo 'are fixed. If some inconsistency remains, it can be orphaned file, deep'
1081 einfo 'dependency, binary package or specially evaluated library.'
1082 if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then
1085 [[ $KEEP_TEMP ]] || rm "${FILES[@]}"
1087 einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'