--- /usr/bin/revdep-rebuild 2011-03-28 09:33:10.000000000 +0300 +++ /dl/revdep-rebuild 2011-03-28 21:42:20.000000000 +0300 @@ -25,6 +25,8 @@ declare -r LDPATH_FILE=2_ldpath.rr # Contains the LDPATH declare -r BROKEN_FILE=3_broken.rr # Contains the list of broken files declare -r ERRORS_FILE=3_errors.rr # Contains the ldd error output +declare -r OWNERSTMP1_FILE=4_ownerstmp1.rr # Contains the list of all variants of filenames we need to resolve +declare -r OWNERSTMP2_FILE=4_ownerstmp2.rr # Contains lines from CONTENTS files, prefixed with package declare -r RAW_FILE=4_raw.rr # Contains the raw list of packages declare -r OWNERS_FILE=4_owners.rr # Contains the file owners declare -r PKGS_FILE=4_pkgs.rr # Contains the unsorted bare package names @@ -38,6 +40,8 @@ "$LDPATH_FILE" "$BROKEN_FILE" "$ERRORS_FILE" + "$OWNERSTMP1_FILE" + "$OWNERSTMP2_FILE" "$RAW_FILE" "$OWNERS_FILE" "$PKGS_FILE" @@ -272,27 +276,69 @@ exit 0 } ## -# Get the name of the package that owns a file or list of files given as args. +# Get the name of the packages that owns each file given as argument. Output: one line per file. +# For a file owned by some package, a line "category/package-version file" is output +# For a file not owned by any package, a line "(none) file" is output +# Verified to work with filenames containing spaces # NOTE: depends on app-misc/realpath! -get_file_owner() { +get_owners_of_files() { + # Set up so that entries are separated by linefeed, for example when using "${*}", but also when parsing output of programs into arrays local IFS=$'\n' - rpath=$(realpath "${*}" 2>/dev/null) - # To ensure we always have something in rpath... - [[ -z $rpath ]] && rpath=${*} - - # Workaround for bug 280341 - mlib=$(echo ${*}|sed 's:/lib/:/lib64/:') - [[ "${*}" == "${mlib}" ]] && mlib=$(echo ${*}|sed 's:/lib64/:/lib/:') - - # Add a space to the end of each object name to prevent false - # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460). - # The same for "${rpath} ". + # NOTE: the use of "${@}" and "${*}" and similar constructs in this function is very delicate. + + # STEP 1. In addition to the listed files we calculate some variants we will be looking for as well. + + # 1. Look for the realpath version as well + local rpathArr=($(realpath "${@}" 2>/dev/null)) + + # 2. Look for libs in both lib and lib64 (bug #280341). We translate both ways to support all cases. + local mlib1Arr=($(echo "${*}"|sed 's:/lib/:/lib64/:')) + local mlib2Arr=($(echo "${*}"|sed 's:/lib64/:/lib/:')) + + # Collect allArr variants into one variable + local allArr=("${@}" "${rpathArr[@]}" "${mlib1Arr[@]}" "${mlib2Arr[@]}") + + # Store a list of all files to look for. Add a space in front and after to prevent false + # matches, for example /usr/bin/dia matching /usr/bin/dialog or /opt/usr/bin/dia (bug #196460). + echo "${allArr[*]}" | sed -e 's!^! !' -e 's!$! !' > "${OWNERSTMP1_FILE}" + + # STEP 2. Scan the CONTENTS files for all matches - at this point we don't detect missing entries yet + # Don't match an entry with a '-' at the start of the package name. This # prevents us from matching invalid -MERGING entries. (bug #338031) - find -L /var/db/pkg -type f -name CONTENTS -print0 | - xargs -0 grep -m 1 -Fl -e "${*} " -e "${rpath} " -e "${mlib} " | - sed 's:/var/db/pkg/\(.*\)/\([^-].*\)/CONTENTS:\1/\2:' + # This is implemented by "-name '-*' -prune -o" in the find command. + + find -L /var/db/pkg -name '-*' -prune -o \ + -type f -name CONTENTS -print0 | + xargs -0 grep -F -f "${OWNERSTMP1_FILE}" | + sed 's!/var/db/pkg/\([^/]*/[^/]*\)/CONTENTS:!\1 !' > "${OWNERSTMP2_FILE}" + + # example content in ${OWNERSTMP2_FILE}: + #media-gfx/xsane-0.996 sym /usr/lib64/gimp/2.0/plug-ins/xsane -> /usr/bin/xsane 1276115338 + #media-gfx/xsane-0.996 obj /usr/bin/xsane f5e07f9209f1b1af817e63fe8c404f4a 1276115335 + + # STEP 3. Now check each function argument separately against the output of step 2, and print results. + + for (( i=0 ; $i<${#@} ; ++i )) ; do + # the index for the positional arguments is one more since they start from 1, while all other arrays start from 0 + local ip1 + (( ip1 = i + 1 )) + local file=${!ip1} + + # calculate rpath again since it may be empty and thus the index in the rpathArr is not direcly reliable + local rpath=$(realpath "${file}" 2>/dev/null) + [ "$rpath" ] || rpath="${file}" + + # find the result from the output of step 2 + local pkg=$(grep -m 1 -F -e " ${file} " -e " ${rpath} " -e "${mlib1Arr[$i]}" -e "${mlib2Arr[$i]}" "${OWNERSTMP2_FILE}" | awk '{print $1}') + # pkg is in format "x11-libs/wxGTK-2.8.10.1-r5" + if [ "$pkg" ]; then + echo "$pkg $file" + else + echo "(none) $file" + fi + done } ## # Normalize some EMERGE_OPTIONS @@ -853,10 +899,10 @@ if [[ -r "$RAW_FILE" && -s "$RAW_FILE" ]]; then einfo "Found existing $RAW_FILE" else - clean_trap "$RAW_FILE" "$OWNERS_FILE" - while read obj target_file; do - EXACT_PKG=$(get_file_owner $target_file) - if [[ $EXACT_PKG ]]; then + clean_trap "${OWNERSTMP1_FILE}" "${OWNERSTMP2_FILE}" "$RAW_FILE" "$OWNERS_FILE" + rebuildList=($(IFS=$'\n' sed 's!^obj !!' < $BROKEN_FILE)) + get_owners_of_files "${rebuildList[@]}" | while read EXACT_PKG target_file; do + if [[ "$EXACT_PKG" != '(none)' ]]; then # Strip version information PKG="${EXACT_PKG%%-r[[:digit:]]*}" PKG="${PKG%-*}" @@ -868,7 +914,7 @@ echo "$target_file -> (none)" >> "$OWNERS_FILE" echo_v " $target_file -> (none)" fi - done < "$BROKEN_FILE" + done [[ $QUIET -ne 1 ]] && einfo "Generated new $RAW_FILE and $OWNERS_FILE" fi # if we find '(none)' on every line, exit out @@ -921,9 +967,9 @@ if [[ -r $EBUILDS_FILE && -s $EBUILDS_FILE ]]; then einfo "Found existing $EBUILDS_FILE" elif [[ -r $BROKEN_FILE && -s $BROKEN_FILE ]]; then - rebuildList=" $(<"$BROKEN_FILE") " - rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ }) - get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$EBUILDS_FILE" + clean_trap "${OWNERSTMP1_FILE}" "${OWNERSTMP2_FILE}" "$EBUILDS_FILE" + rebuildList=($(IFS=$'\n' sed 's!^obj !!' < $BROKEN_FILE)) + get_owners_of_files "${rebuildList[@]}" | egrep -v '^\(none\)' | sed 's/^\([^ ]*\) .*/=\1/' | sort -u > "$EBUILDS_FILE" [[ $QUIET -ne 1 ]] && einfo "Generated new $EBUILDS_FILE" else einfo 'Nothing to rebuild.'