public inbox for gentoo-dev@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-dev] [PATCH] epatch.eclass: Split epatch* logic from eutils
@ 2017-03-11 13:51 Michał Górny
  2017-03-11 14:04 ` Michael Orlitzky
  2017-03-11 15:03 ` [gentoo-dev] [PATCH v2] " Michał Górny
  0 siblings, 2 replies; 5+ messages in thread
From: Michał Górny @ 2017-03-11 13:51 UTC (permalink / raw
  To: gentoo-dev; +Cc: Michał Górny

Move epatch and epatch_user (along with the descriptions for all their
variables) into a dedicated epatch.eclass. This function is very
complex, therefore it benefits from separate eclass and a dedicated
maintainer. Furthermore, it is mostly obsoleted by eapply* in EAPI 6.

The new eclass is implicitly inherited by eutils to preserve
compatibility. However, the inherit will be removed in EAPI 7,
and the ebuilds should switch to inheriting epatch directly or using
eapply*.
-
[Review note: this is 1:1 code move, no changes between the code]
--
 eclass/epatch.eclass | 453 +++++++++++++++++++++++++++++++++++++++++++++++++++
 eclass/eutils.eclass | 440 +------------------------------------------------
 2 files changed, 454 insertions(+), 439 deletions(-)
 create mode 100644 eclass/epatch.eclass

diff --git a/eclass/epatch.eclass b/eclass/epatch.eclass
new file mode 100644
index 000000000000..9727dae6bee7
--- /dev/null
+++ b/eclass/epatch.eclass
@@ -0,0 +1,453 @@
+# Copyright 1999-2017 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: epatch.eclass
+# @MAINTAINER:
+# base-system@gentoo.org
+# @BLURB: easy patch application functions
+# @DESCRIPTION:
+# An eclass providing epatch and epatch_user functions to easily apply
+# patches to ebuilds. Mostly superseded by eapply* in EAPI 6.
+
+# @VARIABLE: EPATCH_SOURCE
+# @DESCRIPTION:
+# Default directory to search for patches.
+EPATCH_SOURCE="${WORKDIR}/patch"
+# @VARIABLE: EPATCH_SUFFIX
+# @DESCRIPTION:
+# Default extension for patches (do not prefix the period yourself).
+EPATCH_SUFFIX="patch.bz2"
+# @VARIABLE: EPATCH_OPTS
+# @DESCRIPTION:
+# Options to pass to patch.  Meant for ebuild/package-specific tweaking
+# such as forcing the patch level (-p#) or fuzz (-F#) factor.  Note that
+# for single patch tweaking, you can also pass flags directly to epatch.
+EPATCH_OPTS=""
+# @VARIABLE: EPATCH_COMMON_OPTS
+# @DESCRIPTION:
+# Common options to pass to `patch`.  You probably should never need to
+# change these.  If you do, please discuss it with base-system first to
+# be sure.
+# @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_COMMON_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.  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) ..."
+# @VARIABLE: EPATCH_FORCE
+# @DESCRIPTION:
+# Only require patches to match EPATCH_SUFFIX rather than the extended
+# arch naming style.
+EPATCH_FORCE="no"
+# @VARIABLE: EPATCH_USER_EXCLUDE
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# List of patches not to apply.	 Note this is only file names,
+# and not the full path.  Globs accepted.
+
+# @FUNCTION: epatch
+# @USAGE: [options] [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 -p4 until things
+# apply successfully.
+#
+# If you do not specify any patches/dirs, then epatch will default to the
+# directory specified by EPATCH_SOURCE.
+#
+# Any options specified that start with a dash will be passed down to patch
+# for this specific invocation.  As soon as an arg w/out a dash is found, then
+# arg processing stops.
+#
+# When processing directories, epatch will apply all patches that match:
+# @CODE
+#	if ${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.
+#
+# 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//?/=}"
+	}
+
+	unset P4CONFIG P4PORT P4USER # keep perforce at bay #56402
+
+	# First process options.  We localize the EPATCH_OPTS setting
+	# from above so that we can pass it on in the loop below with
+	# any additional values the user has specified.
+	local EPATCH_OPTS=( ${EPATCH_OPTS[*]} )
+	while [[ $# -gt 0 ]] ; do
+		case $1 in
+		-*) EPATCH_OPTS+=( "$1" ) ;;
+		*) break ;;
+		esac
+		shift
+	done
+
+	# 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
+
+	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
+
+	elif [[ -d $1 ]] ; then
+		# We have to force sorting to C so that the wildcard expansion is consistent #471666.
+		evar_push_set LC_COLLATE C
+		# Some people like to make dirs of patches w/out suffixes (vim).
+		set -- "$1"/*${EPATCH_SUFFIX:+."${EPATCH_SUFFIX}"}
+		evar_pop
+
+	elif [[ -f ${EPATCH_SOURCE}/$1 ]] ; then
+		# Re-use EPATCH_SOURCE as a search dir
+		epatch "${EPATCH_SOURCE}/$1"
+		return $?
+
+	else
+		# 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
+
+	# Now that we know we're actually going to apply something, merge
+	# all of the patch options back in to a single variable for below.
+	EPATCH_OPTS="${EPATCH_COMMON_OPTS} ${EPATCH_OPTS[*]}"
+
+	local PIPE_CMD
+	case ${EPATCH_SUFFIX##*\.} in
+		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
+
+	[[ ${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
+		# 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
+			continue
+		fi
+
+		# Let people filter things dynamically
+		if [[ -n ${EPATCH_EXCLUDE}${EPATCH_USER_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
+					einfo "  Skipping ${patchname} due to EPATCH_EXCLUDE ..."
+					eshopts_pop
+					continue 2
+				fi
+			done
+
+			for ex in ${EPATCH_USER_EXCLUDE} ; do
+				if [[ ${patchname} == ${ex} ]] ; then
+					einfo "  Skipping ${patchname} due to EPATCH_USER_EXCLUDE ..."
+					eshopts_pop
+					continue 2
+				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
+		else
+			einfo "  ${patchname} ..."
+		fi
+
+		# Handle aliased patch command #404447 #461568
+		local patch="patch"
+		eval $(alias patch 2>/dev/null | sed 's:^alias ::')
+
+		# 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
+
+		printf "***** %s *****\nPWD: %s\nPATCH TOOL: %s -> %s\nVERSION INFO:\n%s\n\n" \
+			"${patchname}" \
+			"${PWD}" \
+			"${patch}" \
+			"$(type -P "${patch}")" \
+			"$(${patch} --version)" \
+			> "${STDERR_TARGET}"
+
+		# 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}"
+
+			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
+
+		# 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
+		# Similar reason, but with relative paths.
+		local rel_paths=$(egrep -n '^[-+]{3} [^	]*[.][.]/' "${PATCH_TARGET}")
+		if [[ -n ${rel_paths} ]] ; then
+			echo
+			eerror "Rejected Patch: ${patchname} !"
+			eerror " ( ${PATCH_TARGET} )"
+			eerror
+			eerror "Your patch uses relative paths '../':"
+			eerror "${rel_paths}"
+			echo
+			die "you need to fix the relative paths in patch"
+		fi
+
+		# Dynamically detect the correct -p# ... i'm lazy, so shoot me :/
+		local patch_cmd
+		while [[ ${count} -lt 5 ]] ; do
+			patch_cmd="${patch} -p${count} ${EPATCH_OPTS}"
+
+			# Generate some useful debug info ...
+			(
+			_epatch_draw_line "***** ${patchname} *****"
+			echo
+			echo "PATCH COMMAND:  ${patch_cmd} --dry-run -f < '${PATCH_TARGET}'"
+			echo
+			_epatch_draw_line "***** ${patchname} *****"
+			${patch_cmd} --dry-run -f < "${PATCH_TARGET}" 2>&1
+			ret=$?
+			echo
+			echo "patch program exited with status ${ret}"
+			exit ${ret}
+			) >> "${STDERR_TARGET}"
+
+			if [ $? -eq 0 ] ; then
+				(
+				_epatch_draw_line "***** ${patchname} *****"
+				echo
+				echo "ACTUALLY APPLYING ${patchname} ..."
+				echo "PATCH COMMAND:  ${patch_cmd} < '${PATCH_TARGET}'"
+				echo
+				_epatch_draw_line "***** ${patchname} *****"
+				${patch_cmd} < "${PATCH_TARGET}" 2>&1
+				ret=$?
+				echo
+				echo "patch program exited with status ${ret}"
+				exit ${ret}
+				) >> "${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
+
+			: $(( count++ ))
+		done
+
+		(( EPATCH_N_APPLIED_PATCHES++ ))
+
+		# if we had to decompress the patch, delete the temp one
+		if [[ -n ${PIPE_CMD} ]] ; then
+			rm -f "${PATCH_TARGET}"
+		fi
+
+		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 full debug patch log
+		rm -f "${STDERR_TARGET}"
+
+		# then log away the exact stuff for people to review later
+		cat <<-EOF >> "${T}/epatch.log"
+		PATCH: ${x}
+		CMD: ${patch_cmd}
+		PWD: ${PWD}
+
+		EOF
+		eend 0
+	done
+
+	[[ ${SINGLE_PATCH} == "no" ]] && einfo "Done with patching"
+	: # everything worked
+}
+
+case ${EAPI:-0} in
+0|1|2|3|4|5)
+
+# @VARIABLE: EPATCH_USER_SOURCE
+# @DESCRIPTION:
+# Location for user patches, see the epatch_user function.
+# Should be set by the user. Don't set this in ebuilds.
+: ${EPATCH_USER_SOURCE:=${PORTAGE_CONFIGROOT%/}/etc/portage/patches}
+
+# @FUNCTION: epatch_user
+# @USAGE:
+# @DESCRIPTION:
+# Applies user-provided patches to the source tree. The patches are
+# taken from /etc/portage/patches/<CATEGORY>/<P-PR|P|PN>[:SLOT]/, where the first
+# of these three directories to exist will be the one to use, ignoring
+# any more general directories which might exist as well. They must end
+# in ".patch" to be applied.
+#
+# User patches are intended for quick testing of patches without ebuild
+# modifications, as well as for permanent customizations a user might
+# desire. Obviously, there can be no official support for arbitrarily
+# patched ebuilds. So whenever a build log in a bug report mentions that
+# user patches were applied, the user should be asked to reproduce the
+# problem without these.
+#
+# Not all ebuilds do call this function, so placing patches in the
+# stated directory might or might not work, depending on the package and
+# the eclasses it inherits and uses. It is safe to call the function
+# repeatedly, so it is always possible to add a call at the ebuild
+# level. The first call is the time when the patches will be
+# applied.
+#
+# Ideally, this function should be called after gentoo-specific patches
+# have been applied, so that their code can be modified as well, but
+# before calls to e.g. eautoreconf, as the user patches might affect
+# autotool input files as well.
+epatch_user() {
+	[[ $# -ne 0 ]] && die "epatch_user takes no options"
+
+	# Allow multiple calls to this function; ignore all but the first
+	local applied="${T}/epatch_user.log"
+	[[ -e ${applied} ]] && return 2
+
+	# don't clobber any EPATCH vars that the parent might want
+	local EPATCH_SOURCE check
+	for check in ${CATEGORY}/{${P}-${PR},${P},${PN}}{,:${SLOT%/*}}; do
+		EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${CTARGET}/${check}
+		[[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${CHOST}/${check}
+		[[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${check}
+		if [[ -d ${EPATCH_SOURCE} ]] ; then
+			local old_n_applied_patches=${EPATCH_N_APPLIED_PATCHES:-0}
+			EPATCH_SOURCE=${EPATCH_SOURCE} \
+			EPATCH_SUFFIX="patch" \
+			EPATCH_FORCE="yes" \
+			EPATCH_MULTI_MSG="Applying user patches from ${EPATCH_SOURCE} ..." \
+			epatch
+			echo "${EPATCH_SOURCE}" > "${applied}"
+			if [[ ${old_n_applied_patches} -lt ${EPATCH_N_APPLIED_PATCHES} ]]; then
+				has epatch_user_death_notice ${EBUILD_DEATH_HOOKS} || \
+					EBUILD_DEATH_HOOKS+=" epatch_user_death_notice"
+			fi
+			return 0
+		fi
+	done
+	echo "none" > "${applied}"
+	return 1
+}
+
+# @FUNCTION: epatch_user_death_notice
+# @INTERNAL
+# @DESCRIPTION:
+# Include an explicit notice in the die message itself that user patches were
+# applied to this build.
+epatch_user_death_notice() {
+	ewarn "!!! User patches were applied to this build!"
+}
+
+esac
diff --git a/eclass/eutils.eclass b/eclass/eutils.eclass
index 7cca864025a6..2726333622bb 100644
--- a/eclass/eutils.eclass
+++ b/eclass/eutils.eclass
@@ -17,7 +17,7 @@
 if [[ -z ${_EUTILS_ECLASS} ]]; then
 _EUTILS_ECLASS=1
 
-inherit estack multilib toolchain-funcs
+inherit epatch estack multilib toolchain-funcs
 
 # @FUNCTION: eqawarn
 # @USAGE: [message]
@@ -63,370 +63,6 @@ egit_clean() {
 	find "$@" -type d -name '.git*' -prune -print0 | xargs -0 rm -rf
 }
 
-# @VARIABLE: EPATCH_SOURCE
-# @DESCRIPTION:
-# Default directory to search for patches.
-EPATCH_SOURCE="${WORKDIR}/patch"
-# @VARIABLE: EPATCH_SUFFIX
-# @DESCRIPTION:
-# Default extension for patches (do not prefix the period yourself).
-EPATCH_SUFFIX="patch.bz2"
-# @VARIABLE: EPATCH_OPTS
-# @DESCRIPTION:
-# Options to pass to patch.  Meant for ebuild/package-specific tweaking
-# such as forcing the patch level (-p#) or fuzz (-F#) factor.  Note that
-# for single patch tweaking, you can also pass flags directly to epatch.
-EPATCH_OPTS=""
-# @VARIABLE: EPATCH_COMMON_OPTS
-# @DESCRIPTION:
-# Common options to pass to `patch`.  You probably should never need to
-# change these.  If you do, please discuss it with base-system first to
-# be sure.
-# @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_COMMON_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.  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) ..."
-# @VARIABLE: EPATCH_FORCE
-# @DESCRIPTION:
-# Only require patches to match EPATCH_SUFFIX rather than the extended
-# arch naming style.
-EPATCH_FORCE="no"
-# @VARIABLE: EPATCH_USER_EXCLUDE
-# @DEFAULT_UNSET
-# @DESCRIPTION:
-# List of patches not to apply.	 Note this is only file names,
-# and not the full path.  Globs accepted.
-
-# @FUNCTION: epatch
-# @USAGE: [options] [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 -p4 until things
-# apply successfully.
-#
-# If you do not specify any patches/dirs, then epatch will default to the
-# directory specified by EPATCH_SOURCE.
-#
-# Any options specified that start with a dash will be passed down to patch
-# for this specific invocation.  As soon as an arg w/out a dash is found, then
-# arg processing stops.
-#
-# When processing directories, epatch will apply all patches that match:
-# @CODE
-#	if ${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.
-#
-# 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//?/=}"
-	}
-
-	unset P4CONFIG P4PORT P4USER # keep perforce at bay #56402
-
-	# First process options.  We localize the EPATCH_OPTS setting
-	# from above so that we can pass it on in the loop below with
-	# any additional values the user has specified.
-	local EPATCH_OPTS=( ${EPATCH_OPTS[*]} )
-	while [[ $# -gt 0 ]] ; do
-		case $1 in
-		-*) EPATCH_OPTS+=( "$1" ) ;;
-		*) break ;;
-		esac
-		shift
-	done
-
-	# 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
-
-	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
-
-	elif [[ -d $1 ]] ; then
-		# We have to force sorting to C so that the wildcard expansion is consistent #471666.
-		evar_push_set LC_COLLATE C
-		# Some people like to make dirs of patches w/out suffixes (vim).
-		set -- "$1"/*${EPATCH_SUFFIX:+."${EPATCH_SUFFIX}"}
-		evar_pop
-
-	elif [[ -f ${EPATCH_SOURCE}/$1 ]] ; then
-		# Re-use EPATCH_SOURCE as a search dir
-		epatch "${EPATCH_SOURCE}/$1"
-		return $?
-
-	else
-		# 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
-
-	# Now that we know we're actually going to apply something, merge
-	# all of the patch options back in to a single variable for below.
-	EPATCH_OPTS="${EPATCH_COMMON_OPTS} ${EPATCH_OPTS[*]}"
-
-	local PIPE_CMD
-	case ${EPATCH_SUFFIX##*\.} in
-		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
-
-	[[ ${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
-		# 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
-			continue
-		fi
-
-		# Let people filter things dynamically
-		if [[ -n ${EPATCH_EXCLUDE}${EPATCH_USER_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
-					einfo "  Skipping ${patchname} due to EPATCH_EXCLUDE ..."
-					eshopts_pop
-					continue 2
-				fi
-			done
-
-			for ex in ${EPATCH_USER_EXCLUDE} ; do
-				if [[ ${patchname} == ${ex} ]] ; then
-					einfo "  Skipping ${patchname} due to EPATCH_USER_EXCLUDE ..."
-					eshopts_pop
-					continue 2
-				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
-		else
-			einfo "  ${patchname} ..."
-		fi
-
-		# Handle aliased patch command #404447 #461568
-		local patch="patch"
-		eval $(alias patch 2>/dev/null | sed 's:^alias ::')
-
-		# 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
-
-		printf "***** %s *****\nPWD: %s\nPATCH TOOL: %s -> %s\nVERSION INFO:\n%s\n\n" \
-			"${patchname}" \
-			"${PWD}" \
-			"${patch}" \
-			"$(type -P "${patch}")" \
-			"$(${patch} --version)" \
-			> "${STDERR_TARGET}"
-
-		# 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}"
-
-			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
-
-		# 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
-		# Similar reason, but with relative paths.
-		local rel_paths=$(egrep -n '^[-+]{3} [^	]*[.][.]/' "${PATCH_TARGET}")
-		if [[ -n ${rel_paths} ]] ; then
-			echo
-			eerror "Rejected Patch: ${patchname} !"
-			eerror " ( ${PATCH_TARGET} )"
-			eerror
-			eerror "Your patch uses relative paths '../':"
-			eerror "${rel_paths}"
-			echo
-			die "you need to fix the relative paths in patch"
-		fi
-
-		# Dynamically detect the correct -p# ... i'm lazy, so shoot me :/
-		local patch_cmd
-		while [[ ${count} -lt 5 ]] ; do
-			patch_cmd="${patch} -p${count} ${EPATCH_OPTS}"
-
-			# Generate some useful debug info ...
-			(
-			_epatch_draw_line "***** ${patchname} *****"
-			echo
-			echo "PATCH COMMAND:  ${patch_cmd} --dry-run -f < '${PATCH_TARGET}'"
-			echo
-			_epatch_draw_line "***** ${patchname} *****"
-			${patch_cmd} --dry-run -f < "${PATCH_TARGET}" 2>&1
-			ret=$?
-			echo
-			echo "patch program exited with status ${ret}"
-			exit ${ret}
-			) >> "${STDERR_TARGET}"
-
-			if [ $? -eq 0 ] ; then
-				(
-				_epatch_draw_line "***** ${patchname} *****"
-				echo
-				echo "ACTUALLY APPLYING ${patchname} ..."
-				echo "PATCH COMMAND:  ${patch_cmd} < '${PATCH_TARGET}'"
-				echo
-				_epatch_draw_line "***** ${patchname} *****"
-				${patch_cmd} < "${PATCH_TARGET}" 2>&1
-				ret=$?
-				echo
-				echo "patch program exited with status ${ret}"
-				exit ${ret}
-				) >> "${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
-
-			: $(( count++ ))
-		done
-
-		(( EPATCH_N_APPLIED_PATCHES++ ))
-
-		# if we had to decompress the patch, delete the temp one
-		if [[ -n ${PIPE_CMD} ]] ; then
-			rm -f "${PATCH_TARGET}"
-		fi
-
-		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 full debug patch log
-		rm -f "${STDERR_TARGET}"
-
-		# then log away the exact stuff for people to review later
-		cat <<-EOF >> "${T}/epatch.log"
-		PATCH: ${x}
-		CMD: ${patch_cmd}
-		PWD: ${PWD}
-
-		EOF
-		eend 0
-	done
-
-	[[ ${SINGLE_PATCH} == "no" ]] && einfo "Done with patching"
-	: # everything worked
-}
-
 # @FUNCTION: emktemp
 # @USAGE: [temp dir]
 # @DESCRIPTION:
@@ -1486,80 +1122,6 @@ esac
 case ${EAPI:-0} in
 0|1|2|3|4|5)
 
-# @VARIABLE: EPATCH_USER_SOURCE
-# @DESCRIPTION:
-# Location for user patches, see the epatch_user function.
-# Should be set by the user. Don't set this in ebuilds.
-: ${EPATCH_USER_SOURCE:=${PORTAGE_CONFIGROOT%/}/etc/portage/patches}
-
-# @FUNCTION: epatch_user
-# @USAGE:
-# @DESCRIPTION:
-# Applies user-provided patches to the source tree. The patches are
-# taken from /etc/portage/patches/<CATEGORY>/<P-PR|P|PN>[:SLOT]/, where the first
-# of these three directories to exist will be the one to use, ignoring
-# any more general directories which might exist as well. They must end
-# in ".patch" to be applied.
-#
-# User patches are intended for quick testing of patches without ebuild
-# modifications, as well as for permanent customizations a user might
-# desire. Obviously, there can be no official support for arbitrarily
-# patched ebuilds. So whenever a build log in a bug report mentions that
-# user patches were applied, the user should be asked to reproduce the
-# problem without these.
-#
-# Not all ebuilds do call this function, so placing patches in the
-# stated directory might or might not work, depending on the package and
-# the eclasses it inherits and uses. It is safe to call the function
-# repeatedly, so it is always possible to add a call at the ebuild
-# level. The first call is the time when the patches will be
-# applied.
-#
-# Ideally, this function should be called after gentoo-specific patches
-# have been applied, so that their code can be modified as well, but
-# before calls to e.g. eautoreconf, as the user patches might affect
-# autotool input files as well.
-epatch_user() {
-	[[ $# -ne 0 ]] && die "epatch_user takes no options"
-
-	# Allow multiple calls to this function; ignore all but the first
-	local applied="${T}/epatch_user.log"
-	[[ -e ${applied} ]] && return 2
-
-	# don't clobber any EPATCH vars that the parent might want
-	local EPATCH_SOURCE check
-	for check in ${CATEGORY}/{${P}-${PR},${P},${PN}}{,:${SLOT%/*}}; do
-		EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${CTARGET}/${check}
-		[[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${CHOST}/${check}
-		[[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${check}
-		if [[ -d ${EPATCH_SOURCE} ]] ; then
-			local old_n_applied_patches=${EPATCH_N_APPLIED_PATCHES:-0}
-			EPATCH_SOURCE=${EPATCH_SOURCE} \
-			EPATCH_SUFFIX="patch" \
-			EPATCH_FORCE="yes" \
-			EPATCH_MULTI_MSG="Applying user patches from ${EPATCH_SOURCE} ..." \
-			epatch
-			echo "${EPATCH_SOURCE}" > "${applied}"
-			if [[ ${old_n_applied_patches} -lt ${EPATCH_N_APPLIED_PATCHES} ]]; then
-				has epatch_user_death_notice ${EBUILD_DEATH_HOOKS} || \
-					EBUILD_DEATH_HOOKS+=" epatch_user_death_notice"
-			fi
-			return 0
-		fi
-	done
-	echo "none" > "${applied}"
-	return 1
-}
-
-# @FUNCTION: epatch_user_death_notice
-# @INTERNAL
-# @DESCRIPTION:
-# Include an explicit notice in the die message itself that user patches were
-# applied to this build.
-epatch_user_death_notice() {
-	ewarn "!!! User patches were applied to this build!"
-}
-
 # @FUNCTION: einstalldocs
 # @DESCRIPTION:
 # Install documentation using DOCS and HTML_DOCS.
-- 
2.12.0



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [gentoo-dev] [PATCH] epatch.eclass: Split epatch* logic from eutils
  2017-03-11 13:51 [gentoo-dev] [PATCH] epatch.eclass: Split epatch* logic from eutils Michał Górny
@ 2017-03-11 14:04 ` Michael Orlitzky
  2017-03-11 14:21   ` Michał Górny
  2017-03-11 15:03 ` [gentoo-dev] [PATCH v2] " Michał Górny
  1 sibling, 1 reply; 5+ messages in thread
From: Michael Orlitzky @ 2017-03-11 14:04 UTC (permalink / raw
  To: gentoo-dev

On 03/11/2017 08:51 AM, Michał Górny wrote:
> 
> However, the inherit will be removed in EAPI 7
> 
> ...
>  
> -inherit estack multilib toolchain-funcs
> +inherit epatch estack multilib toolchain-funcs
>  

Would it hurt to do that now, so that we don't forget when EAPI 7 comes?
For EAPI=0 to 6, inherit them all; otherwise, inherit the old set of
eclasses (without estack or epatch).



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [gentoo-dev] [PATCH] epatch.eclass: Split epatch* logic from eutils
  2017-03-11 14:04 ` Michael Orlitzky
@ 2017-03-11 14:21   ` Michał Górny
  0 siblings, 0 replies; 5+ messages in thread
From: Michał Górny @ 2017-03-11 14:21 UTC (permalink / raw
  To: gentoo-dev

[-- Attachment #1: Type: text/plain, Size: 758 bytes --]

W dniu 11.03.2017, sob o godzinie 09∶04 -0500, użytkownik Michael
Orlitzky napisał:
> On 03/11/2017 08:51 AM, Michał Górny wrote:
> > 
> > However, the inherit will be removed in EAPI 7
> > 
> > ...
> >  
> > -inherit estack multilib toolchain-funcs
> > +inherit epatch estack multilib toolchain-funcs
> >  
> 
> Would it hurt to do that now, so that we don't forget when EAPI 7 comes?
> For EAPI=0 to 6, inherit them all; otherwise, inherit the old set of
> eclasses (without estack or epatch).

Sure, I think we can do that. However, I'd also like to check whether
multilib is currently used at all as that is also a candidate for
removal. So maybe I'll do that in a single followup commit?

-- 
Best regards,
Michał Górny

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 963 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [gentoo-dev] [PATCH v2] epatch.eclass: Split epatch* logic from eutils
  2017-03-11 13:51 [gentoo-dev] [PATCH] epatch.eclass: Split epatch* logic from eutils Michał Górny
  2017-03-11 14:04 ` Michael Orlitzky
@ 2017-03-11 15:03 ` Michał Górny
  2017-03-18  7:34   ` Michał Górny
  1 sibling, 1 reply; 5+ messages in thread
From: Michał Górny @ 2017-03-11 15:03 UTC (permalink / raw
  To: gentoo-dev; +Cc: Michał Górny

Move epatch and epatch_user (along with the descriptions for all their
variables) into a dedicated epatch.eclass. This function is very
complex, therefore it benefits from separate eclass and a dedicated
maintainer. Furthermore, it is mostly obsoleted by eapply* in EAPI 6.

The new eclass is implicitly inherited by eutils to preserve
compatibility. However, the inherit will be removed in EAPI 7,
and the ebuilds should switch to inheriting epatch directly or using
eapply*.

Thanks to Ulrich Müller for doing the necessary research.
---
 eclass/epatch.eclass | 458 +++++++++++++++++++++++++++++++++++++++++++++++++++
 eclass/eutils.eclass | 440 +------------------------------------------------
 2 files changed, 459 insertions(+), 439 deletions(-)
 create mode 100644 eclass/epatch.eclass

diff --git a/eclass/epatch.eclass b/eclass/epatch.eclass
new file mode 100644
index 000000000000..fb0a10b53583
--- /dev/null
+++ b/eclass/epatch.eclass
@@ -0,0 +1,458 @@
+# Copyright 1999-2017 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+# @ECLASS: epatch.eclass
+# @MAINTAINER:
+# base-system@gentoo.org
+# @BLURB: easy patch application functions
+# @DESCRIPTION:
+# An eclass providing epatch and epatch_user functions to easily apply
+# patches to ebuilds. Mostly superseded by eapply* in EAPI 6.
+
+if [[ -z ${_EPATCH_ECLASS} ]]; then
+
+# @VARIABLE: EPATCH_SOURCE
+# @DESCRIPTION:
+# Default directory to search for patches.
+EPATCH_SOURCE="${WORKDIR}/patch"
+# @VARIABLE: EPATCH_SUFFIX
+# @DESCRIPTION:
+# Default extension for patches (do not prefix the period yourself).
+EPATCH_SUFFIX="patch.bz2"
+# @VARIABLE: EPATCH_OPTS
+# @DESCRIPTION:
+# Options to pass to patch.  Meant for ebuild/package-specific tweaking
+# such as forcing the patch level (-p#) or fuzz (-F#) factor.  Note that
+# for single patch tweaking, you can also pass flags directly to epatch.
+EPATCH_OPTS=""
+# @VARIABLE: EPATCH_COMMON_OPTS
+# @DESCRIPTION:
+# Common options to pass to `patch`.  You probably should never need to
+# change these.  If you do, please discuss it with base-system first to
+# be sure.
+# @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_COMMON_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.  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) ..."
+# @VARIABLE: EPATCH_FORCE
+# @DESCRIPTION:
+# Only require patches to match EPATCH_SUFFIX rather than the extended
+# arch naming style.
+EPATCH_FORCE="no"
+# @VARIABLE: EPATCH_USER_EXCLUDE
+# @DEFAULT_UNSET
+# @DESCRIPTION:
+# List of patches not to apply.	 Note this is only file names,
+# and not the full path.  Globs accepted.
+
+# @FUNCTION: epatch
+# @USAGE: [options] [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 -p4 until things
+# apply successfully.
+#
+# If you do not specify any patches/dirs, then epatch will default to the
+# directory specified by EPATCH_SOURCE.
+#
+# Any options specified that start with a dash will be passed down to patch
+# for this specific invocation.  As soon as an arg w/out a dash is found, then
+# arg processing stops.
+#
+# When processing directories, epatch will apply all patches that match:
+# @CODE
+#	if ${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.
+#
+# 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//?/=}"
+	}
+
+	unset P4CONFIG P4PORT P4USER # keep perforce at bay #56402
+
+	# First process options.  We localize the EPATCH_OPTS setting
+	# from above so that we can pass it on in the loop below with
+	# any additional values the user has specified.
+	local EPATCH_OPTS=( ${EPATCH_OPTS[*]} )
+	while [[ $# -gt 0 ]] ; do
+		case $1 in
+		-*) EPATCH_OPTS+=( "$1" ) ;;
+		*) break ;;
+		esac
+		shift
+	done
+
+	# 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
+
+	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
+
+	elif [[ -d $1 ]] ; then
+		# We have to force sorting to C so that the wildcard expansion is consistent #471666.
+		evar_push_set LC_COLLATE C
+		# Some people like to make dirs of patches w/out suffixes (vim).
+		set -- "$1"/*${EPATCH_SUFFIX:+."${EPATCH_SUFFIX}"}
+		evar_pop
+
+	elif [[ -f ${EPATCH_SOURCE}/$1 ]] ; then
+		# Re-use EPATCH_SOURCE as a search dir
+		epatch "${EPATCH_SOURCE}/$1"
+		return $?
+
+	else
+		# 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
+
+	# Now that we know we're actually going to apply something, merge
+	# all of the patch options back in to a single variable for below.
+	EPATCH_OPTS="${EPATCH_COMMON_OPTS} ${EPATCH_OPTS[*]}"
+
+	local PIPE_CMD
+	case ${EPATCH_SUFFIX##*\.} in
+		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
+
+	[[ ${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
+		# 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
+			continue
+		fi
+
+		# Let people filter things dynamically
+		if [[ -n ${EPATCH_EXCLUDE}${EPATCH_USER_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
+					einfo "  Skipping ${patchname} due to EPATCH_EXCLUDE ..."
+					eshopts_pop
+					continue 2
+				fi
+			done
+
+			for ex in ${EPATCH_USER_EXCLUDE} ; do
+				if [[ ${patchname} == ${ex} ]] ; then
+					einfo "  Skipping ${patchname} due to EPATCH_USER_EXCLUDE ..."
+					eshopts_pop
+					continue 2
+				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
+		else
+			einfo "  ${patchname} ..."
+		fi
+
+		# Handle aliased patch command #404447 #461568
+		local patch="patch"
+		eval $(alias patch 2>/dev/null | sed 's:^alias ::')
+
+		# 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
+
+		printf "***** %s *****\nPWD: %s\nPATCH TOOL: %s -> %s\nVERSION INFO:\n%s\n\n" \
+			"${patchname}" \
+			"${PWD}" \
+			"${patch}" \
+			"$(type -P "${patch}")" \
+			"$(${patch} --version)" \
+			> "${STDERR_TARGET}"
+
+		# 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}"
+
+			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
+
+		# 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
+		# Similar reason, but with relative paths.
+		local rel_paths=$(egrep -n '^[-+]{3} [^	]*[.][.]/' "${PATCH_TARGET}")
+		if [[ -n ${rel_paths} ]] ; then
+			echo
+			eerror "Rejected Patch: ${patchname} !"
+			eerror " ( ${PATCH_TARGET} )"
+			eerror
+			eerror "Your patch uses relative paths '../':"
+			eerror "${rel_paths}"
+			echo
+			die "you need to fix the relative paths in patch"
+		fi
+
+		# Dynamically detect the correct -p# ... i'm lazy, so shoot me :/
+		local patch_cmd
+		while [[ ${count} -lt 5 ]] ; do
+			patch_cmd="${patch} -p${count} ${EPATCH_OPTS}"
+
+			# Generate some useful debug info ...
+			(
+			_epatch_draw_line "***** ${patchname} *****"
+			echo
+			echo "PATCH COMMAND:  ${patch_cmd} --dry-run -f < '${PATCH_TARGET}'"
+			echo
+			_epatch_draw_line "***** ${patchname} *****"
+			${patch_cmd} --dry-run -f < "${PATCH_TARGET}" 2>&1
+			ret=$?
+			echo
+			echo "patch program exited with status ${ret}"
+			exit ${ret}
+			) >> "${STDERR_TARGET}"
+
+			if [ $? -eq 0 ] ; then
+				(
+				_epatch_draw_line "***** ${patchname} *****"
+				echo
+				echo "ACTUALLY APPLYING ${patchname} ..."
+				echo "PATCH COMMAND:  ${patch_cmd} < '${PATCH_TARGET}'"
+				echo
+				_epatch_draw_line "***** ${patchname} *****"
+				${patch_cmd} < "${PATCH_TARGET}" 2>&1
+				ret=$?
+				echo
+				echo "patch program exited with status ${ret}"
+				exit ${ret}
+				) >> "${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
+
+			: $(( count++ ))
+		done
+
+		(( EPATCH_N_APPLIED_PATCHES++ ))
+
+		# if we had to decompress the patch, delete the temp one
+		if [[ -n ${PIPE_CMD} ]] ; then
+			rm -f "${PATCH_TARGET}"
+		fi
+
+		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 full debug patch log
+		rm -f "${STDERR_TARGET}"
+
+		# then log away the exact stuff for people to review later
+		cat <<-EOF >> "${T}/epatch.log"
+		PATCH: ${x}
+		CMD: ${patch_cmd}
+		PWD: ${PWD}
+
+		EOF
+		eend 0
+	done
+
+	[[ ${SINGLE_PATCH} == "no" ]] && einfo "Done with patching"
+	: # everything worked
+}
+
+case ${EAPI:-0} in
+0|1|2|3|4|5)
+
+# @VARIABLE: EPATCH_USER_SOURCE
+# @DESCRIPTION:
+# Location for user patches, see the epatch_user function.
+# Should be set by the user. Don't set this in ebuilds.
+: ${EPATCH_USER_SOURCE:=${PORTAGE_CONFIGROOT%/}/etc/portage/patches}
+
+# @FUNCTION: epatch_user
+# @USAGE:
+# @DESCRIPTION:
+# Applies user-provided patches to the source tree. The patches are
+# taken from /etc/portage/patches/<CATEGORY>/<P-PR|P|PN>[:SLOT]/, where the first
+# of these three directories to exist will be the one to use, ignoring
+# any more general directories which might exist as well. They must end
+# in ".patch" to be applied.
+#
+# User patches are intended for quick testing of patches without ebuild
+# modifications, as well as for permanent customizations a user might
+# desire. Obviously, there can be no official support for arbitrarily
+# patched ebuilds. So whenever a build log in a bug report mentions that
+# user patches were applied, the user should be asked to reproduce the
+# problem without these.
+#
+# Not all ebuilds do call this function, so placing patches in the
+# stated directory might or might not work, depending on the package and
+# the eclasses it inherits and uses. It is safe to call the function
+# repeatedly, so it is always possible to add a call at the ebuild
+# level. The first call is the time when the patches will be
+# applied.
+#
+# Ideally, this function should be called after gentoo-specific patches
+# have been applied, so that their code can be modified as well, but
+# before calls to e.g. eautoreconf, as the user patches might affect
+# autotool input files as well.
+epatch_user() {
+	[[ $# -ne 0 ]] && die "epatch_user takes no options"
+
+	# Allow multiple calls to this function; ignore all but the first
+	local applied="${T}/epatch_user.log"
+	[[ -e ${applied} ]] && return 2
+
+	# don't clobber any EPATCH vars that the parent might want
+	local EPATCH_SOURCE check
+	for check in ${CATEGORY}/{${P}-${PR},${P},${PN}}{,:${SLOT%/*}}; do
+		EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${CTARGET}/${check}
+		[[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${CHOST}/${check}
+		[[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${check}
+		if [[ -d ${EPATCH_SOURCE} ]] ; then
+			local old_n_applied_patches=${EPATCH_N_APPLIED_PATCHES:-0}
+			EPATCH_SOURCE=${EPATCH_SOURCE} \
+			EPATCH_SUFFIX="patch" \
+			EPATCH_FORCE="yes" \
+			EPATCH_MULTI_MSG="Applying user patches from ${EPATCH_SOURCE} ..." \
+			epatch
+			echo "${EPATCH_SOURCE}" > "${applied}"
+			if [[ ${old_n_applied_patches} -lt ${EPATCH_N_APPLIED_PATCHES} ]]; then
+				has epatch_user_death_notice ${EBUILD_DEATH_HOOKS} || \
+					EBUILD_DEATH_HOOKS+=" epatch_user_death_notice"
+			fi
+			return 0
+		fi
+	done
+	echo "none" > "${applied}"
+	return 1
+}
+
+# @FUNCTION: epatch_user_death_notice
+# @INTERNAL
+# @DESCRIPTION:
+# Include an explicit notice in the die message itself that user patches were
+# applied to this build.
+epatch_user_death_notice() {
+	ewarn "!!! User patches were applied to this build!"
+}
+
+esac
+
+_EPATCH_ECLASS=1
+fi #_EPATCH_ECLASS
diff --git a/eclass/eutils.eclass b/eclass/eutils.eclass
index 79ec00042a3f..724a310b3000 100644
--- a/eclass/eutils.eclass
+++ b/eclass/eutils.eclass
@@ -22,7 +22,7 @@ inherit multilib toolchain-funcs
 # implicitly inherited (now split) eclasses
 case ${EAPI:-0} in
 0|1|2|3|4|5|6)
-	inherit estack
+	inherit epatch estack
 	;;
 esac
 
@@ -70,370 +70,6 @@ egit_clean() {
 	find "$@" -type d -name '.git*' -prune -print0 | xargs -0 rm -rf
 }
 
-# @VARIABLE: EPATCH_SOURCE
-# @DESCRIPTION:
-# Default directory to search for patches.
-EPATCH_SOURCE="${WORKDIR}/patch"
-# @VARIABLE: EPATCH_SUFFIX
-# @DESCRIPTION:
-# Default extension for patches (do not prefix the period yourself).
-EPATCH_SUFFIX="patch.bz2"
-# @VARIABLE: EPATCH_OPTS
-# @DESCRIPTION:
-# Options to pass to patch.  Meant for ebuild/package-specific tweaking
-# such as forcing the patch level (-p#) or fuzz (-F#) factor.  Note that
-# for single patch tweaking, you can also pass flags directly to epatch.
-EPATCH_OPTS=""
-# @VARIABLE: EPATCH_COMMON_OPTS
-# @DESCRIPTION:
-# Common options to pass to `patch`.  You probably should never need to
-# change these.  If you do, please discuss it with base-system first to
-# be sure.
-# @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_COMMON_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.  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) ..."
-# @VARIABLE: EPATCH_FORCE
-# @DESCRIPTION:
-# Only require patches to match EPATCH_SUFFIX rather than the extended
-# arch naming style.
-EPATCH_FORCE="no"
-# @VARIABLE: EPATCH_USER_EXCLUDE
-# @DEFAULT_UNSET
-# @DESCRIPTION:
-# List of patches not to apply.	 Note this is only file names,
-# and not the full path.  Globs accepted.
-
-# @FUNCTION: epatch
-# @USAGE: [options] [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 -p4 until things
-# apply successfully.
-#
-# If you do not specify any patches/dirs, then epatch will default to the
-# directory specified by EPATCH_SOURCE.
-#
-# Any options specified that start with a dash will be passed down to patch
-# for this specific invocation.  As soon as an arg w/out a dash is found, then
-# arg processing stops.
-#
-# When processing directories, epatch will apply all patches that match:
-# @CODE
-#	if ${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.
-#
-# 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//?/=}"
-	}
-
-	unset P4CONFIG P4PORT P4USER # keep perforce at bay #56402
-
-	# First process options.  We localize the EPATCH_OPTS setting
-	# from above so that we can pass it on in the loop below with
-	# any additional values the user has specified.
-	local EPATCH_OPTS=( ${EPATCH_OPTS[*]} )
-	while [[ $# -gt 0 ]] ; do
-		case $1 in
-		-*) EPATCH_OPTS+=( "$1" ) ;;
-		*) break ;;
-		esac
-		shift
-	done
-
-	# 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
-
-	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
-
-	elif [[ -d $1 ]] ; then
-		# We have to force sorting to C so that the wildcard expansion is consistent #471666.
-		evar_push_set LC_COLLATE C
-		# Some people like to make dirs of patches w/out suffixes (vim).
-		set -- "$1"/*${EPATCH_SUFFIX:+."${EPATCH_SUFFIX}"}
-		evar_pop
-
-	elif [[ -f ${EPATCH_SOURCE}/$1 ]] ; then
-		# Re-use EPATCH_SOURCE as a search dir
-		epatch "${EPATCH_SOURCE}/$1"
-		return $?
-
-	else
-		# 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
-
-	# Now that we know we're actually going to apply something, merge
-	# all of the patch options back in to a single variable for below.
-	EPATCH_OPTS="${EPATCH_COMMON_OPTS} ${EPATCH_OPTS[*]}"
-
-	local PIPE_CMD
-	case ${EPATCH_SUFFIX##*\.} in
-		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
-
-	[[ ${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
-		# 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
-			continue
-		fi
-
-		# Let people filter things dynamically
-		if [[ -n ${EPATCH_EXCLUDE}${EPATCH_USER_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
-					einfo "  Skipping ${patchname} due to EPATCH_EXCLUDE ..."
-					eshopts_pop
-					continue 2
-				fi
-			done
-
-			for ex in ${EPATCH_USER_EXCLUDE} ; do
-				if [[ ${patchname} == ${ex} ]] ; then
-					einfo "  Skipping ${patchname} due to EPATCH_USER_EXCLUDE ..."
-					eshopts_pop
-					continue 2
-				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
-		else
-			einfo "  ${patchname} ..."
-		fi
-
-		# Handle aliased patch command #404447 #461568
-		local patch="patch"
-		eval $(alias patch 2>/dev/null | sed 's:^alias ::')
-
-		# 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
-
-		printf "***** %s *****\nPWD: %s\nPATCH TOOL: %s -> %s\nVERSION INFO:\n%s\n\n" \
-			"${patchname}" \
-			"${PWD}" \
-			"${patch}" \
-			"$(type -P "${patch}")" \
-			"$(${patch} --version)" \
-			> "${STDERR_TARGET}"
-
-		# 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}"
-
-			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
-
-		# 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
-		# Similar reason, but with relative paths.
-		local rel_paths=$(egrep -n '^[-+]{3} [^	]*[.][.]/' "${PATCH_TARGET}")
-		if [[ -n ${rel_paths} ]] ; then
-			echo
-			eerror "Rejected Patch: ${patchname} !"
-			eerror " ( ${PATCH_TARGET} )"
-			eerror
-			eerror "Your patch uses relative paths '../':"
-			eerror "${rel_paths}"
-			echo
-			die "you need to fix the relative paths in patch"
-		fi
-
-		# Dynamically detect the correct -p# ... i'm lazy, so shoot me :/
-		local patch_cmd
-		while [[ ${count} -lt 5 ]] ; do
-			patch_cmd="${patch} -p${count} ${EPATCH_OPTS}"
-
-			# Generate some useful debug info ...
-			(
-			_epatch_draw_line "***** ${patchname} *****"
-			echo
-			echo "PATCH COMMAND:  ${patch_cmd} --dry-run -f < '${PATCH_TARGET}'"
-			echo
-			_epatch_draw_line "***** ${patchname} *****"
-			${patch_cmd} --dry-run -f < "${PATCH_TARGET}" 2>&1
-			ret=$?
-			echo
-			echo "patch program exited with status ${ret}"
-			exit ${ret}
-			) >> "${STDERR_TARGET}"
-
-			if [ $? -eq 0 ] ; then
-				(
-				_epatch_draw_line "***** ${patchname} *****"
-				echo
-				echo "ACTUALLY APPLYING ${patchname} ..."
-				echo "PATCH COMMAND:  ${patch_cmd} < '${PATCH_TARGET}'"
-				echo
-				_epatch_draw_line "***** ${patchname} *****"
-				${patch_cmd} < "${PATCH_TARGET}" 2>&1
-				ret=$?
-				echo
-				echo "patch program exited with status ${ret}"
-				exit ${ret}
-				) >> "${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
-
-			: $(( count++ ))
-		done
-
-		(( EPATCH_N_APPLIED_PATCHES++ ))
-
-		# if we had to decompress the patch, delete the temp one
-		if [[ -n ${PIPE_CMD} ]] ; then
-			rm -f "${PATCH_TARGET}"
-		fi
-
-		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 full debug patch log
-		rm -f "${STDERR_TARGET}"
-
-		# then log away the exact stuff for people to review later
-		cat <<-EOF >> "${T}/epatch.log"
-		PATCH: ${x}
-		CMD: ${patch_cmd}
-		PWD: ${PWD}
-
-		EOF
-		eend 0
-	done
-
-	[[ ${SINGLE_PATCH} == "no" ]] && einfo "Done with patching"
-	: # everything worked
-}
-
 # @FUNCTION: emktemp
 # @USAGE: [temp dir]
 # @DESCRIPTION:
@@ -1493,80 +1129,6 @@ esac
 case ${EAPI:-0} in
 0|1|2|3|4|5)
 
-# @VARIABLE: EPATCH_USER_SOURCE
-# @DESCRIPTION:
-# Location for user patches, see the epatch_user function.
-# Should be set by the user. Don't set this in ebuilds.
-: ${EPATCH_USER_SOURCE:=${PORTAGE_CONFIGROOT%/}/etc/portage/patches}
-
-# @FUNCTION: epatch_user
-# @USAGE:
-# @DESCRIPTION:
-# Applies user-provided patches to the source tree. The patches are
-# taken from /etc/portage/patches/<CATEGORY>/<P-PR|P|PN>[:SLOT]/, where the first
-# of these three directories to exist will be the one to use, ignoring
-# any more general directories which might exist as well. They must end
-# in ".patch" to be applied.
-#
-# User patches are intended for quick testing of patches without ebuild
-# modifications, as well as for permanent customizations a user might
-# desire. Obviously, there can be no official support for arbitrarily
-# patched ebuilds. So whenever a build log in a bug report mentions that
-# user patches were applied, the user should be asked to reproduce the
-# problem without these.
-#
-# Not all ebuilds do call this function, so placing patches in the
-# stated directory might or might not work, depending on the package and
-# the eclasses it inherits and uses. It is safe to call the function
-# repeatedly, so it is always possible to add a call at the ebuild
-# level. The first call is the time when the patches will be
-# applied.
-#
-# Ideally, this function should be called after gentoo-specific patches
-# have been applied, so that their code can be modified as well, but
-# before calls to e.g. eautoreconf, as the user patches might affect
-# autotool input files as well.
-epatch_user() {
-	[[ $# -ne 0 ]] && die "epatch_user takes no options"
-
-	# Allow multiple calls to this function; ignore all but the first
-	local applied="${T}/epatch_user.log"
-	[[ -e ${applied} ]] && return 2
-
-	# don't clobber any EPATCH vars that the parent might want
-	local EPATCH_SOURCE check
-	for check in ${CATEGORY}/{${P}-${PR},${P},${PN}}{,:${SLOT%/*}}; do
-		EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${CTARGET}/${check}
-		[[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${CHOST}/${check}
-		[[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${check}
-		if [[ -d ${EPATCH_SOURCE} ]] ; then
-			local old_n_applied_patches=${EPATCH_N_APPLIED_PATCHES:-0}
-			EPATCH_SOURCE=${EPATCH_SOURCE} \
-			EPATCH_SUFFIX="patch" \
-			EPATCH_FORCE="yes" \
-			EPATCH_MULTI_MSG="Applying user patches from ${EPATCH_SOURCE} ..." \
-			epatch
-			echo "${EPATCH_SOURCE}" > "${applied}"
-			if [[ ${old_n_applied_patches} -lt ${EPATCH_N_APPLIED_PATCHES} ]]; then
-				has epatch_user_death_notice ${EBUILD_DEATH_HOOKS} || \
-					EBUILD_DEATH_HOOKS+=" epatch_user_death_notice"
-			fi
-			return 0
-		fi
-	done
-	echo "none" > "${applied}"
-	return 1
-}
-
-# @FUNCTION: epatch_user_death_notice
-# @INTERNAL
-# @DESCRIPTION:
-# Include an explicit notice in the die message itself that user patches were
-# applied to this build.
-epatch_user_death_notice() {
-	ewarn "!!! User patches were applied to this build!"
-}
-
 # @FUNCTION: einstalldocs
 # @DESCRIPTION:
 # Install documentation using DOCS and HTML_DOCS.
-- 
2.12.0



^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [gentoo-dev] [PATCH v2] epatch.eclass: Split epatch* logic from eutils
  2017-03-11 15:03 ` [gentoo-dev] [PATCH v2] " Michał Górny
@ 2017-03-18  7:34   ` Michał Górny
  0 siblings, 0 replies; 5+ messages in thread
From: Michał Górny @ 2017-03-18  7:34 UTC (permalink / raw
  To: gentoo-dev

[-- Attachment #1: Type: text/plain, Size: 33636 bytes --]

On sob, 2017-03-11 at 16:03 +0100, Michał Górny wrote:
> Move epatch and epatch_user (along with the descriptions for all their
> variables) into a dedicated epatch.eclass. This function is very
> complex, therefore it benefits from separate eclass and a dedicated
> maintainer. Furthermore, it is mostly obsoleted by eapply* in EAPI 6.
> 
> The new eclass is implicitly inherited by eutils to preserve
> compatibility. However, the inherit will be removed in EAPI 7,
> and the ebuilds should switch to inheriting epatch directly or using
> eapply*.
> 
> Thanks to Ulrich Müller for doing the necessary research.
> ---
>  eclass/epatch.eclass | 458 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  eclass/eutils.eclass | 440 +------------------------------------------------
>  2 files changed, 459 insertions(+), 439 deletions(-)
>  create mode 100644 eclass/epatch.eclass
> 
> diff --git a/eclass/epatch.eclass b/eclass/epatch.eclass
> new file mode 100644
> index 000000000000..fb0a10b53583
> --- /dev/null
> +++ b/eclass/epatch.eclass
> @@ -0,0 +1,458 @@
> +# Copyright 1999-2017 Gentoo Foundation
> +# Distributed under the terms of the GNU General Public License v2
> +
> +# @ECLASS: epatch.eclass
> +# @MAINTAINER:
> +# base-system@gentoo.org
> +# @BLURB: easy patch application functions
> +# @DESCRIPTION:
> +# An eclass providing epatch and epatch_user functions to easily apply
> +# patches to ebuilds. Mostly superseded by eapply* in EAPI 6.
> +
> +if [[ -z ${_EPATCH_ECLASS} ]]; then
> +
> +# @VARIABLE: EPATCH_SOURCE
> +# @DESCRIPTION:
> +# Default directory to search for patches.
> +EPATCH_SOURCE="${WORKDIR}/patch"
> +# @VARIABLE: EPATCH_SUFFIX
> +# @DESCRIPTION:
> +# Default extension for patches (do not prefix the period yourself).
> +EPATCH_SUFFIX="patch.bz2"
> +# @VARIABLE: EPATCH_OPTS
> +# @DESCRIPTION:
> +# Options to pass to patch.  Meant for ebuild/package-specific tweaking
> +# such as forcing the patch level (-p#) or fuzz (-F#) factor.  Note that
> +# for single patch tweaking, you can also pass flags directly to epatch.
> +EPATCH_OPTS=""
> +# @VARIABLE: EPATCH_COMMON_OPTS
> +# @DESCRIPTION:
> +# Common options to pass to `patch`.  You probably should never need to
> +# change these.  If you do, please discuss it with base-system first to
> +# be sure.
> +# @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_COMMON_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.  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) ..."
> +# @VARIABLE: EPATCH_FORCE
> +# @DESCRIPTION:
> +# Only require patches to match EPATCH_SUFFIX rather than the extended
> +# arch naming style.
> +EPATCH_FORCE="no"
> +# @VARIABLE: EPATCH_USER_EXCLUDE
> +# @DEFAULT_UNSET
> +# @DESCRIPTION:
> +# List of patches not to apply.	 Note this is only file names,
> +# and not the full path.  Globs accepted.
> +
> +# @FUNCTION: epatch
> +# @USAGE: [options] [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 -p4 until things
> +# apply successfully.
> +#
> +# If you do not specify any patches/dirs, then epatch will default to the
> +# directory specified by EPATCH_SOURCE.
> +#
> +# Any options specified that start with a dash will be passed down to patch
> +# for this specific invocation.  As soon as an arg w/out a dash is found, then
> +# arg processing stops.
> +#
> +# When processing directories, epatch will apply all patches that match:
> +# @CODE
> +#	if ${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.
> +#
> +# 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//?/=}"
> +	}
> +
> +	unset P4CONFIG P4PORT P4USER # keep perforce at bay #56402
> +
> +	# First process options.  We localize the EPATCH_OPTS setting
> +	# from above so that we can pass it on in the loop below with
> +	# any additional values the user has specified.
> +	local EPATCH_OPTS=( ${EPATCH_OPTS[*]} )
> +	while [[ $# -gt 0 ]] ; do
> +		case $1 in
> +		-*) EPATCH_OPTS+=( "$1" ) ;;
> +		*) break ;;
> +		esac
> +		shift
> +	done
> +
> +	# 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
> +
> +	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
> +
> +	elif [[ -d $1 ]] ; then
> +		# We have to force sorting to C so that the wildcard expansion is consistent #471666.
> +		evar_push_set LC_COLLATE C
> +		# Some people like to make dirs of patches w/out suffixes (vim).
> +		set -- "$1"/*${EPATCH_SUFFIX:+."${EPATCH_SUFFIX}"}
> +		evar_pop
> +
> +	elif [[ -f ${EPATCH_SOURCE}/$1 ]] ; then
> +		# Re-use EPATCH_SOURCE as a search dir
> +		epatch "${EPATCH_SOURCE}/$1"
> +		return $?
> +
> +	else
> +		# 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
> +
> +	# Now that we know we're actually going to apply something, merge
> +	# all of the patch options back in to a single variable for below.
> +	EPATCH_OPTS="${EPATCH_COMMON_OPTS} ${EPATCH_OPTS[*]}"
> +
> +	local PIPE_CMD
> +	case ${EPATCH_SUFFIX##*\.} in
> +		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
> +
> +	[[ ${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
> +		# 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
> +			continue
> +		fi
> +
> +		# Let people filter things dynamically
> +		if [[ -n ${EPATCH_EXCLUDE}${EPATCH_USER_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
> +					einfo "  Skipping ${patchname} due to EPATCH_EXCLUDE ..."
> +					eshopts_pop
> +					continue 2
> +				fi
> +			done
> +
> +			for ex in ${EPATCH_USER_EXCLUDE} ; do
> +				if [[ ${patchname} == ${ex} ]] ; then
> +					einfo "  Skipping ${patchname} due to EPATCH_USER_EXCLUDE ..."
> +					eshopts_pop
> +					continue 2
> +				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
> +		else
> +			einfo "  ${patchname} ..."
> +		fi
> +
> +		# Handle aliased patch command #404447 #461568
> +		local patch="patch"
> +		eval $(alias patch 2>/dev/null | sed 's:^alias ::')
> +
> +		# 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
> +
> +		printf "***** %s *****\nPWD: %s\nPATCH TOOL: %s -> %s\nVERSION INFO:\n%s\n\n" \
> +			"${patchname}" \
> +			"${PWD}" \
> +			"${patch}" \
> +			"$(type -P "${patch}")" \
> +			"$(${patch} --version)" \
> +			> "${STDERR_TARGET}"
> +
> +		# 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}"
> +
> +			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
> +
> +		# 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
> +		# Similar reason, but with relative paths.
> +		local rel_paths=$(egrep -n '^[-+]{3} [^	]*[.][.]/' "${PATCH_TARGET}")
> +		if [[ -n ${rel_paths} ]] ; then
> +			echo
> +			eerror "Rejected Patch: ${patchname} !"
> +			eerror " ( ${PATCH_TARGET} )"
> +			eerror
> +			eerror "Your patch uses relative paths '../':"
> +			eerror "${rel_paths}"
> +			echo
> +			die "you need to fix the relative paths in patch"
> +		fi
> +
> +		# Dynamically detect the correct -p# ... i'm lazy, so shoot me :/
> +		local patch_cmd
> +		while [[ ${count} -lt 5 ]] ; do
> +			patch_cmd="${patch} -p${count} ${EPATCH_OPTS}"
> +
> +			# Generate some useful debug info ...
> +			(
> +			_epatch_draw_line "***** ${patchname} *****"
> +			echo
> +			echo "PATCH COMMAND:  ${patch_cmd} --dry-run -f < '${PATCH_TARGET}'"
> +			echo
> +			_epatch_draw_line "***** ${patchname} *****"
> +			${patch_cmd} --dry-run -f < "${PATCH_TARGET}" 2>&1
> +			ret=$?
> +			echo
> +			echo "patch program exited with status ${ret}"
> +			exit ${ret}
> +			) >> "${STDERR_TARGET}"
> +
> +			if [ $? -eq 0 ] ; then
> +				(
> +				_epatch_draw_line "***** ${patchname} *****"
> +				echo
> +				echo "ACTUALLY APPLYING ${patchname} ..."
> +				echo "PATCH COMMAND:  ${patch_cmd} < '${PATCH_TARGET}'"
> +				echo
> +				_epatch_draw_line "***** ${patchname} *****"
> +				${patch_cmd} < "${PATCH_TARGET}" 2>&1
> +				ret=$?
> +				echo
> +				echo "patch program exited with status ${ret}"
> +				exit ${ret}
> +				) >> "${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
> +
> +			: $(( count++ ))
> +		done
> +
> +		(( EPATCH_N_APPLIED_PATCHES++ ))
> +
> +		# if we had to decompress the patch, delete the temp one
> +		if [[ -n ${PIPE_CMD} ]] ; then
> +			rm -f "${PATCH_TARGET}"
> +		fi
> +
> +		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 full debug patch log
> +		rm -f "${STDERR_TARGET}"
> +
> +		# then log away the exact stuff for people to review later
> +		cat <<-EOF >> "${T}/epatch.log"
> +		PATCH: ${x}
> +		CMD: ${patch_cmd}
> +		PWD: ${PWD}
> +
> +		EOF
> +		eend 0
> +	done
> +
> +	[[ ${SINGLE_PATCH} == "no" ]] && einfo "Done with patching"
> +	: # everything worked
> +}
> +
> +case ${EAPI:-0} in
> +0|1|2|3|4|5)
> +
> +# @VARIABLE: EPATCH_USER_SOURCE
> +# @DESCRIPTION:
> +# Location for user patches, see the epatch_user function.
> +# Should be set by the user. Don't set this in ebuilds.
> +: ${EPATCH_USER_SOURCE:=${PORTAGE_CONFIGROOT%/}/etc/portage/patches}
> +
> +# @FUNCTION: epatch_user
> +# @USAGE:
> +# @DESCRIPTION:
> +# Applies user-provided patches to the source tree. The patches are
> +# taken from /etc/portage/patches/<CATEGORY>/<P-PR|P|PN>[:SLOT]/, where the first
> +# of these three directories to exist will be the one to use, ignoring
> +# any more general directories which might exist as well. They must end
> +# in ".patch" to be applied.
> +#
> +# User patches are intended for quick testing of patches without ebuild
> +# modifications, as well as for permanent customizations a user might
> +# desire. Obviously, there can be no official support for arbitrarily
> +# patched ebuilds. So whenever a build log in a bug report mentions that
> +# user patches were applied, the user should be asked to reproduce the
> +# problem without these.
> +#
> +# Not all ebuilds do call this function, so placing patches in the
> +# stated directory might or might not work, depending on the package and
> +# the eclasses it inherits and uses. It is safe to call the function
> +# repeatedly, so it is always possible to add a call at the ebuild
> +# level. The first call is the time when the patches will be
> +# applied.
> +#
> +# Ideally, this function should be called after gentoo-specific patches
> +# have been applied, so that their code can be modified as well, but
> +# before calls to e.g. eautoreconf, as the user patches might affect
> +# autotool input files as well.
> +epatch_user() {
> +	[[ $# -ne 0 ]] && die "epatch_user takes no options"
> +
> +	# Allow multiple calls to this function; ignore all but the first
> +	local applied="${T}/epatch_user.log"
> +	[[ -e ${applied} ]] && return 2
> +
> +	# don't clobber any EPATCH vars that the parent might want
> +	local EPATCH_SOURCE check
> +	for check in ${CATEGORY}/{${P}-${PR},${P},${PN}}{,:${SLOT%/*}}; do
> +		EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${CTARGET}/${check}
> +		[[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${CHOST}/${check}
> +		[[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${check}
> +		if [[ -d ${EPATCH_SOURCE} ]] ; then
> +			local old_n_applied_patches=${EPATCH_N_APPLIED_PATCHES:-0}
> +			EPATCH_SOURCE=${EPATCH_SOURCE} \
> +			EPATCH_SUFFIX="patch" \
> +			EPATCH_FORCE="yes" \
> +			EPATCH_MULTI_MSG="Applying user patches from ${EPATCH_SOURCE} ..." \
> +			epatch
> +			echo "${EPATCH_SOURCE}" > "${applied}"
> +			if [[ ${old_n_applied_patches} -lt ${EPATCH_N_APPLIED_PATCHES} ]]; then
> +				has epatch_user_death_notice ${EBUILD_DEATH_HOOKS} || \
> +					EBUILD_DEATH_HOOKS+=" epatch_user_death_notice"
> +			fi
> +			return 0
> +		fi
> +	done
> +	echo "none" > "${applied}"
> +	return 1
> +}
> +
> +# @FUNCTION: epatch_user_death_notice
> +# @INTERNAL
> +# @DESCRIPTION:
> +# Include an explicit notice in the die message itself that user patches were
> +# applied to this build.
> +epatch_user_death_notice() {
> +	ewarn "!!! User patches were applied to this build!"
> +}
> +
> +esac
> +
> +_EPATCH_ECLASS=1
> +fi #_EPATCH_ECLASS
> diff --git a/eclass/eutils.eclass b/eclass/eutils.eclass
> index 79ec00042a3f..724a310b3000 100644
> --- a/eclass/eutils.eclass
> +++ b/eclass/eutils.eclass
> @@ -22,7 +22,7 @@ inherit multilib toolchain-funcs
>  # implicitly inherited (now split) eclasses
>  case ${EAPI:-0} in
>  0|1|2|3|4|5|6)
> -	inherit estack
> +	inherit epatch estack
>  	;;
>  esac
>  
> @@ -70,370 +70,6 @@ egit_clean() {
>  	find "$@" -type d -name '.git*' -prune -print0 | xargs -0 rm -rf
>  }
>  
> -# @VARIABLE: EPATCH_SOURCE
> -# @DESCRIPTION:
> -# Default directory to search for patches.
> -EPATCH_SOURCE="${WORKDIR}/patch"
> -# @VARIABLE: EPATCH_SUFFIX
> -# @DESCRIPTION:
> -# Default extension for patches (do not prefix the period yourself).
> -EPATCH_SUFFIX="patch.bz2"
> -# @VARIABLE: EPATCH_OPTS
> -# @DESCRIPTION:
> -# Options to pass to patch.  Meant for ebuild/package-specific tweaking
> -# such as forcing the patch level (-p#) or fuzz (-F#) factor.  Note that
> -# for single patch tweaking, you can also pass flags directly to epatch.
> -EPATCH_OPTS=""
> -# @VARIABLE: EPATCH_COMMON_OPTS
> -# @DESCRIPTION:
> -# Common options to pass to `patch`.  You probably should never need to
> -# change these.  If you do, please discuss it with base-system first to
> -# be sure.
> -# @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_COMMON_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.  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) ..."
> -# @VARIABLE: EPATCH_FORCE
> -# @DESCRIPTION:
> -# Only require patches to match EPATCH_SUFFIX rather than the extended
> -# arch naming style.
> -EPATCH_FORCE="no"
> -# @VARIABLE: EPATCH_USER_EXCLUDE
> -# @DEFAULT_UNSET
> -# @DESCRIPTION:
> -# List of patches not to apply.	 Note this is only file names,
> -# and not the full path.  Globs accepted.
> -
> -# @FUNCTION: epatch
> -# @USAGE: [options] [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 -p4 until things
> -# apply successfully.
> -#
> -# If you do not specify any patches/dirs, then epatch will default to the
> -# directory specified by EPATCH_SOURCE.
> -#
> -# Any options specified that start with a dash will be passed down to patch
> -# for this specific invocation.  As soon as an arg w/out a dash is found, then
> -# arg processing stops.
> -#
> -# When processing directories, epatch will apply all patches that match:
> -# @CODE
> -#	if ${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.
> -#
> -# 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//?/=}"
> -	}
> -
> -	unset P4CONFIG P4PORT P4USER # keep perforce at bay #56402
> -
> -	# First process options.  We localize the EPATCH_OPTS setting
> -	# from above so that we can pass it on in the loop below with
> -	# any additional values the user has specified.
> -	local EPATCH_OPTS=( ${EPATCH_OPTS[*]} )
> -	while [[ $# -gt 0 ]] ; do
> -		case $1 in
> -		-*) EPATCH_OPTS+=( "$1" ) ;;
> -		*) break ;;
> -		esac
> -		shift
> -	done
> -
> -	# 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
> -
> -	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
> -
> -	elif [[ -d $1 ]] ; then
> -		# We have to force sorting to C so that the wildcard expansion is consistent #471666.
> -		evar_push_set LC_COLLATE C
> -		# Some people like to make dirs of patches w/out suffixes (vim).
> -		set -- "$1"/*${EPATCH_SUFFIX:+."${EPATCH_SUFFIX}"}
> -		evar_pop
> -
> -	elif [[ -f ${EPATCH_SOURCE}/$1 ]] ; then
> -		# Re-use EPATCH_SOURCE as a search dir
> -		epatch "${EPATCH_SOURCE}/$1"
> -		return $?
> -
> -	else
> -		# 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
> -
> -	# Now that we know we're actually going to apply something, merge
> -	# all of the patch options back in to a single variable for below.
> -	EPATCH_OPTS="${EPATCH_COMMON_OPTS} ${EPATCH_OPTS[*]}"
> -
> -	local PIPE_CMD
> -	case ${EPATCH_SUFFIX##*\.} in
> -		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
> -
> -	[[ ${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
> -		# 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
> -			continue
> -		fi
> -
> -		# Let people filter things dynamically
> -		if [[ -n ${EPATCH_EXCLUDE}${EPATCH_USER_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
> -					einfo "  Skipping ${patchname} due to EPATCH_EXCLUDE ..."
> -					eshopts_pop
> -					continue 2
> -				fi
> -			done
> -
> -			for ex in ${EPATCH_USER_EXCLUDE} ; do
> -				if [[ ${patchname} == ${ex} ]] ; then
> -					einfo "  Skipping ${patchname} due to EPATCH_USER_EXCLUDE ..."
> -					eshopts_pop
> -					continue 2
> -				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
> -		else
> -			einfo "  ${patchname} ..."
> -		fi
> -
> -		# Handle aliased patch command #404447 #461568
> -		local patch="patch"
> -		eval $(alias patch 2>/dev/null | sed 's:^alias ::')
> -
> -		# 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
> -
> -		printf "***** %s *****\nPWD: %s\nPATCH TOOL: %s -> %s\nVERSION INFO:\n%s\n\n" \
> -			"${patchname}" \
> -			"${PWD}" \
> -			"${patch}" \
> -			"$(type -P "${patch}")" \
> -			"$(${patch} --version)" \
> -			> "${STDERR_TARGET}"
> -
> -		# 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}"
> -
> -			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
> -
> -		# 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
> -		# Similar reason, but with relative paths.
> -		local rel_paths=$(egrep -n '^[-+]{3} [^	]*[.][.]/' "${PATCH_TARGET}")
> -		if [[ -n ${rel_paths} ]] ; then
> -			echo
> -			eerror "Rejected Patch: ${patchname} !"
> -			eerror " ( ${PATCH_TARGET} )"
> -			eerror
> -			eerror "Your patch uses relative paths '../':"
> -			eerror "${rel_paths}"
> -			echo
> -			die "you need to fix the relative paths in patch"
> -		fi
> -
> -		# Dynamically detect the correct -p# ... i'm lazy, so shoot me :/
> -		local patch_cmd
> -		while [[ ${count} -lt 5 ]] ; do
> -			patch_cmd="${patch} -p${count} ${EPATCH_OPTS}"
> -
> -			# Generate some useful debug info ...
> -			(
> -			_epatch_draw_line "***** ${patchname} *****"
> -			echo
> -			echo "PATCH COMMAND:  ${patch_cmd} --dry-run -f < '${PATCH_TARGET}'"
> -			echo
> -			_epatch_draw_line "***** ${patchname} *****"
> -			${patch_cmd} --dry-run -f < "${PATCH_TARGET}" 2>&1
> -			ret=$?
> -			echo
> -			echo "patch program exited with status ${ret}"
> -			exit ${ret}
> -			) >> "${STDERR_TARGET}"
> -
> -			if [ $? -eq 0 ] ; then
> -				(
> -				_epatch_draw_line "***** ${patchname} *****"
> -				echo
> -				echo "ACTUALLY APPLYING ${patchname} ..."
> -				echo "PATCH COMMAND:  ${patch_cmd} < '${PATCH_TARGET}'"
> -				echo
> -				_epatch_draw_line "***** ${patchname} *****"
> -				${patch_cmd} < "${PATCH_TARGET}" 2>&1
> -				ret=$?
> -				echo
> -				echo "patch program exited with status ${ret}"
> -				exit ${ret}
> -				) >> "${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
> -
> -			: $(( count++ ))
> -		done
> -
> -		(( EPATCH_N_APPLIED_PATCHES++ ))
> -
> -		# if we had to decompress the patch, delete the temp one
> -		if [[ -n ${PIPE_CMD} ]] ; then
> -			rm -f "${PATCH_TARGET}"
> -		fi
> -
> -		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 full debug patch log
> -		rm -f "${STDERR_TARGET}"
> -
> -		# then log away the exact stuff for people to review later
> -		cat <<-EOF >> "${T}/epatch.log"
> -		PATCH: ${x}
> -		CMD: ${patch_cmd}
> -		PWD: ${PWD}
> -
> -		EOF
> -		eend 0
> -	done
> -
> -	[[ ${SINGLE_PATCH} == "no" ]] && einfo "Done with patching"
> -	: # everything worked
> -}
> -
>  # @FUNCTION: emktemp
>  # @USAGE: [temp dir]
>  # @DESCRIPTION:
> @@ -1493,80 +1129,6 @@ esac
>  case ${EAPI:-0} in
>  0|1|2|3|4|5)
>  
> -# @VARIABLE: EPATCH_USER_SOURCE
> -# @DESCRIPTION:
> -# Location for user patches, see the epatch_user function.
> -# Should be set by the user. Don't set this in ebuilds.
> -: ${EPATCH_USER_SOURCE:=${PORTAGE_CONFIGROOT%/}/etc/portage/patches}
> -
> -# @FUNCTION: epatch_user
> -# @USAGE:
> -# @DESCRIPTION:
> -# Applies user-provided patches to the source tree. The patches are
> -# taken from /etc/portage/patches/<CATEGORY>/<P-PR|P|PN>[:SLOT]/, where the first
> -# of these three directories to exist will be the one to use, ignoring
> -# any more general directories which might exist as well. They must end
> -# in ".patch" to be applied.
> -#
> -# User patches are intended for quick testing of patches without ebuild
> -# modifications, as well as for permanent customizations a user might
> -# desire. Obviously, there can be no official support for arbitrarily
> -# patched ebuilds. So whenever a build log in a bug report mentions that
> -# user patches were applied, the user should be asked to reproduce the
> -# problem without these.
> -#
> -# Not all ebuilds do call this function, so placing patches in the
> -# stated directory might or might not work, depending on the package and
> -# the eclasses it inherits and uses. It is safe to call the function
> -# repeatedly, so it is always possible to add a call at the ebuild
> -# level. The first call is the time when the patches will be
> -# applied.
> -#
> -# Ideally, this function should be called after gentoo-specific patches
> -# have been applied, so that their code can be modified as well, but
> -# before calls to e.g. eautoreconf, as the user patches might affect
> -# autotool input files as well.
> -epatch_user() {
> -	[[ $# -ne 0 ]] && die "epatch_user takes no options"
> -
> -	# Allow multiple calls to this function; ignore all but the first
> -	local applied="${T}/epatch_user.log"
> -	[[ -e ${applied} ]] && return 2
> -
> -	# don't clobber any EPATCH vars that the parent might want
> -	local EPATCH_SOURCE check
> -	for check in ${CATEGORY}/{${P}-${PR},${P},${PN}}{,:${SLOT%/*}}; do
> -		EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${CTARGET}/${check}
> -		[[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${CHOST}/${check}
> -		[[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${EPATCH_USER_SOURCE}/${check}
> -		if [[ -d ${EPATCH_SOURCE} ]] ; then
> -			local old_n_applied_patches=${EPATCH_N_APPLIED_PATCHES:-0}
> -			EPATCH_SOURCE=${EPATCH_SOURCE} \
> -			EPATCH_SUFFIX="patch" \
> -			EPATCH_FORCE="yes" \
> -			EPATCH_MULTI_MSG="Applying user patches from ${EPATCH_SOURCE} ..." \
> -			epatch
> -			echo "${EPATCH_SOURCE}" > "${applied}"
> -			if [[ ${old_n_applied_patches} -lt ${EPATCH_N_APPLIED_PATCHES} ]]; then
> -				has epatch_user_death_notice ${EBUILD_DEATH_HOOKS} || \
> -					EBUILD_DEATH_HOOKS+=" epatch_user_death_notice"
> -			fi
> -			return 0
> -		fi
> -	done
> -	echo "none" > "${applied}"
> -	return 1
> -}
> -
> -# @FUNCTION: epatch_user_death_notice
> -# @INTERNAL
> -# @DESCRIPTION:
> -# Include an explicit notice in the die message itself that user patches were
> -# applied to this build.
> -epatch_user_death_notice() {
> -	ewarn "!!! User patches were applied to this build!"
> -}
> -
>  # @FUNCTION: einstalldocs
>  # @DESCRIPTION:
>  # Install documentation using DOCS and HTML_DOCS.

Merged.
-- 
Best regards,
Michał Górny

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 963 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2017-03-18  7:35 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-03-11 13:51 [gentoo-dev] [PATCH] epatch.eclass: Split epatch* logic from eutils Michał Górny
2017-03-11 14:04 ` Michael Orlitzky
2017-03-11 14:21   ` Michał Górny
2017-03-11 15:03 ` [gentoo-dev] [PATCH v2] " Michał Górny
2017-03-18  7:34   ` Michał Górny

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox