diff options
author | Mike Frysinger <vapier@gentoo.org> | 2010-01-09 20:06:24 +0000 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2010-01-09 20:06:24 +0000 |
commit | 2975c21eefa60655a341e12e5389c6e990edd6c4 (patch) | |
tree | 1d24d2b1e34d39da42016d32f02722f4f439de31 /eclass/eutils.eclass | |
parent | Version bump. (diff) | |
download | historical-2975c21eefa60655a341e12e5389c6e990edd6c4.tar.gz historical-2975c21eefa60655a341e12e5389c6e990edd6c4.tar.bz2 historical-2975c21eefa60655a341e12e5389c6e990edd6c4.zip |
rewrite epatch greatly -- clean up logging, quoting, shell opts, file name checking, and decompression
Diffstat (limited to 'eclass/eutils.eclass')
-rw-r--r-- | eclass/eutils.eclass | 424 |
1 files changed, 206 insertions, 218 deletions
diff --git a/eclass/eutils.eclass b/eclass/eutils.eclass index 2be2bf88d903..9c4e07224426 100644 --- a/eclass/eutils.eclass +++ b/eclass/eutils.eclass @@ -1,6 +1,6 @@ # Copyright 1999-2009 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# $Header: /var/cvsroot/gentoo-x86/eclass/eutils.eclass,v 1.324 2010/01/03 19:57:10 zmedico Exp $ +# $Header: /var/cvsroot/gentoo-x86/eclass/eutils.eclass,v 1.325 2010/01/09 20:06:24 vapier Exp $ # @ECLASS: eutils.eclass # @MAINTAINER: @@ -114,284 +114,272 @@ eshopts_pop() { set -${s} || die "eshopts_pop: sanity: unable to restore saved shell settings: ${s}" } -# Default directory where patches are located +# @VARIABLE: EPATCH_SOURCE +# @DESCRIPTION: +# Default directory to search for patches. EPATCH_SOURCE="${WORKDIR}/patch" -# Default extension for patches +# @VARIABLE: EPATCH_SUFFIX +# @DESCRIPTION: +# Default extension for patches (do not prefix the period yourself). EPATCH_SUFFIX="patch.bz2" -# Default options for patch -# Set -g0 to keep RCS, ClearCase, Perforce and SCCS happy. Bug #24571 -# Set --no-backup-if-mismatch so we don't leave '.orig' files behind. -# Set -E to automatically remove empty files. +# @VARIABLE: EPATCH_OPTS +# @DESCRIPTION: +# Default options for patch: +# @CODE +# -g0 - keep RCS, ClearCase, Perforce and SCCS happy #24571 +# --no-backup-if-mismatch - do not leave .orig files behind +# -E - automatically remove empty files +# @CODE EPATCH_OPTS="-g0 -E --no-backup-if-mismatch" +# @VARIABLE: EPATCH_EXCLUDE +# @DESCRIPTION: # List of patches not to apply. Note this is only file names, -# and not the full path .. +# and not the full path. Globs accepted. EPATCH_EXCLUDE="" +# @VARIABLE: EPATCH_SINGLE_MSG +# @DESCRIPTION: # Change the printed message for a single patch. EPATCH_SINGLE_MSG="" +# @VARIABLE: EPATCH_MULTI_MSG +# @DESCRIPTION: # Change the printed message for multiple patches. EPATCH_MULTI_MSG="Applying various patches (bugfixes/updates) ..." -# Force applying bulk patches even if not following the style: -# -# ??_${ARCH}_foo.${EPATCH_SUFFIX} -# +# @VARIABLE: EPATCH_FORCE +# @DESCRIPTION: +# Only require patches to match EPATCH_SUFFIX rather than the extended +# arch naming style. EPATCH_FORCE="no" -# This function is for bulk patching, or in theory for just one -# or two patches. -# -# It should work with .bz2, .gz, .zip and plain text patches. -# Currently all patches should be the same format. -# -# You do not have to specify '-p' option to patch, as it will -# try with -p0 to -p5 until it succeed, or fail at -p5. -# -# Above EPATCH_* variables can be used to control various defaults, -# bug they should be left as is to ensure an ebuild can rely on -# them for. -# -# Patches are applied in current directory. -# -# Bulk Patches should preferably have the form of: -# -# ??_${ARCH}_foo.${EPATCH_SUFFIX} -# -# For example: -# -# 01_all_misc-fix.patch.bz2 -# 02_sparc_another-fix.patch.bz2 +# @FUNCTION: epatch +# @USAGE: [patches] [dirs of patches] +# @DESCRIPTION: +# epatch is designed to greatly simplify the application of patches. It can +# process patch files directly, or directories of patches. The patches may be +# compressed (bzip/gzip/etc...) or plain text. You generally need not specify +# the -p option as epatch will automatically attempt -p0 to -p5 until things +# apply successfully. # -# This ensures that there are a set order, and you can have ARCH -# specific patches. +# If you do not specify any options, then epatch will default to the directory +# specified by EPATCH_SOURCE. # -# If you however give an argument to epatch(), it will treat it as a -# single patch that need to be applied if its a file. If on the other -# hand its a directory, it will set EPATCH_SOURCE to this. +# When processing directories, epatch will apply all patches that match: +# @CODE +# ${EPATCH_FORCE} == "yes" +# ??_${ARCH}_foo.${EPATCH_SUFFIX} +# else +# *.${EPATCH_SUFFIX} +# @CODE +# The leading ?? are typically numbers used to force consistent patch ordering. +# The arch field is used to apply patches only for the host architecture with +# the special value of "all" means apply for everyone. Note that using values +# other than "all" is highly discouraged -- you should apply patches all the +# time and let architecture details be detected at configure/compile time. # -# <azarah@gentoo.org> (10 Nov 2002) +# If EPATCH_SUFFIX is empty, then no period before it is implied when searching +# for patches to apply. # +# Refer to the other EPATCH_xxx variables for more customization of behavior. epatch() { _epatch_draw_line() { + # create a line of same length as input string [[ -z $1 ]] && set "$(printf "%65s" '')" echo "${1//?/=}" } - _epatch_assert() { local _pipestatus=${PIPESTATUS[*]}; [[ ${_pipestatus// /} -eq 0 ]] ; } - local PIPE_CMD="" - local STDERR_TARGET="${T}/$$.out" - local PATCH_TARGET="${T}/$$.patch" - local PATCH_SUFFIX="" - local SINGLE_PATCH="no" - local x="" unset P4CONFIG P4PORT P4USER # keep perforce at bay #56402 - if [ "$#" -gt 1 ] - then - local m="" + # Let the rest of the code process one user arg at a time -- + # each arg may expand into multiple patches, and each arg may + # need to start off with the default global EPATCH_xxx values + if [[ $# -gt 1 ]] ; then + local m for m in "$@" ; do epatch "${m}" done return 0 fi - if [ -n "$1" -a -f "$1" ] - then + local SINGLE_PATCH="no" + # no args means process ${EPATCH_SOURCE} + [[ $# -eq 0 ]] && set -- "${EPATCH_SOURCE}" + + if [[ -f $1 ]] ; then SINGLE_PATCH="yes" + set -- "$1" + # Use the suffix from the single patch (localize it); the code + # below will find the suffix for us + local EPATCH_SUFFIX=$1 - local EPATCH_SOURCE="$1" - local EPATCH_SUFFIX="${1##*\.}" + elif [[ -d $1 ]] ; then + # Some people like to make dirs of patches w/out suffixes (vim) + set -- "$1"/*${EPATCH_SUFFIX:+."${EPATCH_SUFFIX}"} - elif [ -n "$1" -a -d "$1" ] - then - # Allow no extension if EPATCH_FORCE=yes ... used by vim for example ... - if [ "${EPATCH_FORCE}" = "yes" ] && [ -z "${EPATCH_SUFFIX}" ] - then - local EPATCH_SOURCE="$1/*" - else - local EPATCH_SOURCE="$1/*.${EPATCH_SUFFIX}" - fi else - if [[ ! -d ${EPATCH_SOURCE} ]] || [[ -n $1 ]] ; then - if [ -n "$1" -a "${EPATCH_SOURCE}" = "${WORKDIR}/patch" ] - then - EPATCH_SOURCE="$1" - fi - - echo - eerror "Cannot find \$EPATCH_SOURCE! Value for \$EPATCH_SOURCE is:" - eerror - eerror " ${EPATCH_SOURCE}" - eerror " ( ${EPATCH_SOURCE##*/} )" - echo - die "Cannot find \$EPATCH_SOURCE!" - fi - - local EPATCH_SOURCE="${EPATCH_SOURCE}/*.${EPATCH_SUFFIX}" + # sanity check ... if it isn't a dir or file, wtf man ? + [[ $# -ne 0 ]] && EPATCH_SOURCE=$1 + echo + eerror "Cannot find \$EPATCH_SOURCE! Value for \$EPATCH_SOURCE is:" + eerror + eerror " ${EPATCH_SOURCE}" + eerror " ( ${EPATCH_SOURCE##*/} )" + echo + die "Cannot find \$EPATCH_SOURCE!" fi + local PIPE_CMD case ${EPATCH_SUFFIX##*\.} in - xz) - PIPE_CMD="xz -dc" - PATCH_SUFFIX="xz" - ;; - lzma) - PIPE_CMD="lzma -dc" - PATCH_SUFFIX="lzma" - ;; - bz2) - PIPE_CMD="bzip2 -dc" - PATCH_SUFFIX="bz2" - ;; - gz|Z|z) - PIPE_CMD="gzip -dc" - PATCH_SUFFIX="gz" - ;; - ZIP|zip) - PIPE_CMD="unzip -p" - PATCH_SUFFIX="zip" - ;; - *) - PIPE_CMD="cat" - PATCH_SUFFIX="patch" - ;; + xz) PIPE_CMD="xz -dc" ;; + lzma) PIPE_CMD="lzma -dc" ;; + bz2) PIPE_CMD="bzip2 -dc" ;; + gz|Z|z) PIPE_CMD="gzip -dc" ;; + ZIP|zip) PIPE_CMD="unzip -p" ;; + *) ;; esac - if [ "${SINGLE_PATCH}" = "no" ] - then - einfo "${EPATCH_MULTI_MSG}" - fi - for x in ${EPATCH_SOURCE} - do - # New ARCH dependant patch naming scheme ... - # + [[ ${SINGLE_PATCH} == "no" ]] && einfo "${EPATCH_MULTI_MSG}" + + local x + for x in "$@" ; do + # If the patch dir given contains subdirs, or our EPATCH_SUFFIX + # didn't match anything, ignore continue on + [[ ! -f ${x} ]] && continue + + local patchname=${x##*/} + + # Apply single patches, or forced sets of patches, or + # patches with ARCH dependant names. # ???_arch_foo.patch - # - if [ -f ${x} ] && \ - ([ "${SINGLE_PATCH}" = "yes" -o "${x/_all_}" != "${x}" -o "${x/_${ARCH}_}" != "${x}" ] || \ - [ "${EPATCH_FORCE}" = "yes" ]) + # Else, skip this input altogether + local a=${patchname#*_} # strip the ???_ + a=${a%%_*} # strip the _foo.patch + if ! [[ ${SINGLE_PATCH} == "yes" || \ + ${EPATCH_FORCE} == "yes" || \ + ${a} == all || \ + ${a} == ${ARCH} ]] then - local count=0 - local popts="${EPATCH_OPTS}" - local patchname=${x##*/} + continue + fi - if [ -n "${EPATCH_EXCLUDE}" ] - then - if [ "${EPATCH_EXCLUDE/${patchname}}" != "${EPATCH_EXCLUDE}" ] - then + # Let people filter things dynamically + if [[ -n ${EPATCH_EXCLUDE} ]] ; then + # let people use globs in the exclude + eshopts_push -o noglob + + local ex + for ex in ${EPATCH_EXCLUDE} ; do + if [[ ${patchname} == ${ex} ]] ; then + eshopts_pop continue fi - fi + done + eshopts_pop + fi - if [ "${SINGLE_PATCH}" = "yes" ] - then - if [ -n "${EPATCH_SINGLE_MSG}" ] - then - einfo "${EPATCH_SINGLE_MSG}" - else - einfo "Applying ${patchname} ..." - fi + if [[ ${SINGLE_PATCH} == "yes" ]] ; then + if [[ -n ${EPATCH_SINGLE_MSG} ]] ; then + einfo "${EPATCH_SINGLE_MSG}" else - einfo " ${patchname} ..." + einfo "Applying ${patchname} ..." fi + else + einfo " ${patchname} ..." + fi - echo "***** ${patchname} *****" > ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - echo >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} + # most of the time, there will only be one run per unique name, + # but if there are more, make sure we get unique log filenames + local STDERR_TARGET="${T}/${patchname}.out" + if [[ -e ${STDERR_TARGET} ]] ; then + STDERR_TARGET="${T}/${patchname}-$$.out" + fi - # Decompress the patch if need be - if [[ ${PATCH_SUFFIX} != "patch" ]] ; then - echo -n "PIPE_COMMAND: " >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - echo "${PIPE_CMD} ${x} > ${PATCH_TARGET}" >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} + printf "***** %s *****\n\n" "${patchname}" > "${STDERR_TARGET}" - if ! (${PIPE_CMD} ${x} > ${PATCH_TARGET}) >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} 2>&1 ; then - echo - eerror "Could not extract patch!" - #die "Could not extract patch!" - count=5 - break - fi - else - PATCH_TARGET="${x}" - fi + # Decompress the patch if need be + local count=0 + local PATCH_TARGET + if [[ -n ${PIPE_CMD} ]] ; then + PATCH_TARGET="${T}/$$.patch" + echo "PIPE_COMMAND: ${PIPE_CMD} ${x} > ${PATCH_TARGET}" >> "${STDERR_TARGET}" - # Check for absolute paths in patches. If sandbox is disabled, - # people could (accidently) patch files in the root filesystem. - # Or trigger other unpleasantries #237667. So disallow -p0 on - # such patches. - local abs_paths=$(egrep -n '^[-+]{3} /' "${PATCH_TARGET}" | awk '$2 != "/dev/null" { print }') - if [[ -n ${abs_paths} ]] ; then - count=1 - echo "NOTE: skipping -p0 due to absolute paths in patch:" >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - echo "${abs_paths}" >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} + if ! (${PIPE_CMD} "${x}" > "${PATCH_TARGET}") >> "${STDERR_TARGET}" 2>&1 ; then + echo + eerror "Could not extract patch!" + #die "Could not extract patch!" + count=5 + break fi + else + PATCH_TARGET=${x} + fi - # Allow for prefix to differ ... im lazy, so shoot me :/ - while [ "${count}" -lt 5 ] - do - # Generate some useful debug info ... - _epatch_draw_line "***** ${patchname} *****" >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - echo >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - - echo -n "PATCH COMMAND: " >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - echo "patch -p${count} ${popts} < ${PATCH_TARGET}" >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - - echo >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - _epatch_draw_line "***** ${patchname} *****" >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - - if (cat ${PATCH_TARGET} | patch -p${count} ${popts} --dry-run -f ; _epatch_assert) >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} 2>&1 - then - _epatch_draw_line "***** ${patchname} *****" > ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real - echo >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real - echo "ACTUALLY APPLYING ${patchname} ..." >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real - echo >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real - _epatch_draw_line "***** ${patchname} *****" >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real - - cat ${PATCH_TARGET} | patch -p${count} ${popts} >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real 2>&1 - _epatch_assert - - if [ "$?" -ne 0 ] - then - cat ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - echo - eerror "A dry-run of patch command succeeded, but actually" - eerror "applying the patch failed!" - #die "Real world sux compared to the dreamworld!" - count=5 - fi - - rm -f ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real - - break - fi - - count=$((count + 1)) - done + # Check for absolute paths in patches. If sandbox is disabled, + # people could (accidently) patch files in the root filesystem. + # Or trigger other unpleasantries #237667. So disallow -p0 on + # such patches. + local abs_paths=$(egrep -n '^[-+]{3} /' "${PATCH_TARGET}" | awk '$2 != "/dev/null" { print }') + if [[ -n ${abs_paths} ]] ; then + count=1 + printf "NOTE: skipping -p0 due to absolute paths in patch:\n%s\n" "${abs_paths}" >> "${STDERR_TARGET}" + fi - if [ "${PATCH_SUFFIX}" != "patch" ] - then - rm -f ${PATCH_TARGET} - fi + # Dynamically detect the correct -p# ... i'm lazy, so shoot me :/ + while [[ ${count} -lt 5 ]] ; do + # Generate some useful debug info ... + ( + _epatch_draw_line "***** ${patchname} *****" + echo + echo "PATCH COMMAND: patch -p${count} ${EPATCH_OPTS} < '${PATCH_TARGET}'" + echo + _epatch_draw_line "***** ${patchname} *****" + ) >> "${STDERR_TARGET}" - if [ "${count}" -eq 5 ] - then + if (patch -p${count} ${EPATCH_OPTS} --dry-run -f < "${PATCH_TARGET}") >> "${STDERR_TARGET}" 2>&1 ; then + ( + _epatch_draw_line "***** ${patchname} *****" echo - eerror "Failed Patch: ${patchname} !" - eerror " ( ${PATCH_TARGET} )" - eerror - eerror "Include in your bugreport the contents of:" - eerror - eerror " ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}" + echo "ACTUALLY APPLYING ${patchname} ..." echo - die "Failed Patch: ${patchname}!" + _epatch_draw_line "***** ${patchname} *****" + patch -p${count} ${EPATCH_OPTS} < "${PATCH_TARGET}" 2>&1 + ) >> "${STDERR_TARGET}" + + if [ $? -ne 0 ] ; then + echo + eerror "A dry-run of patch command succeeded, but actually" + eerror "applying the patch failed!" + #die "Real world sux compared to the dreamworld!" + count=5 + fi + break fi - rm -f ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} + : $(( count++ )) + done + + # if we had to decompress the patch, delete the temp one + if [[ -n ${PIPE_CMD} ]] ; then + rm -f "${PATCH_TARGET}" + fi - eend 0 + if [[ ${count} -ge 5 ]] ; then + echo + eerror "Failed Patch: ${patchname} !" + eerror " ( ${PATCH_TARGET} )" + eerror + eerror "Include in your bugreport the contents of:" + eerror + eerror " ${STDERR_TARGET}" + echo + die "Failed Patch: ${patchname}!" fi + + # if everything worked, delete the patch log + rm -f "${STDERR_TARGET}" + eend 0 done - if [ "${SINGLE_PATCH}" = "no" ] - then - einfo "Done with patching" - fi + + [[ ${SINGLE_PATCH} == "no" ]] && einfo "Done with patching" + : # everything worked } epatch_user() { [[ $# -ne 0 ]] && die "epatch_user takes no options" |