#!/usr/bin/env bash
# Copyright 1999-2023 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
# shellcheck disable=1007,2128

source "${PORTAGE_BIN_PATH:?}"/helper-functions.sh || exit

# Atomically writes the standard input to a file whose name is formatted as
# "estrip-%u.warning", <checksum of input>. The existing contents of the file,
# if any, shall not be preserved.
stash_warning() {
	local tempfile crc

	tempfile=$(mktemp -- "${T:-/tmp}/estrip.XXXXXX") \
	&& crc=$(set -o pipefail; tee -- "${tempfile}" | cksum) \
	&& mv -- "${tempfile}" "${T}/estrip-${crc% *}.warning"
}

# Iterates over any files previously created by the stash_warning() function,
# conveying their respective lines through the invocation of ewarn.
raise_warnings() {
	local logfile

	for logfile in "${T:?}"/estrip-*.warning; do
		test -f "${logfile}" || continue
		while read -r; do
			ewarn "${REPLY}"
		done < "${logfile}"
	done
}

# Usage: save_elf_sources <elf>
save_elf_sources() {
	local x=$1

	# shellcheck disable=2317
	if (( ! has_feature[installsources] || has_restriction[installsources] )); then
		save_elf_sources() { :; }
		return
	elif ! hash "${name_of[debugedit]}" 2>/dev/null; then
		stash_warning <<-'EOF'
		FEATURES=installsources requires the debugedit binary, which was not found!
		EOF
		save_elf_sources() { :; }
		return
	fi

	# since we're editing the ELF here, we should recompute the build-id
	# (the -i flag below).  save that output so we don't need to recompute
	# it later on in the save_elf_debug step.
	buildid=$("${name_of[debugedit]}" -i \
		-s "${CATEGORY}/${PF}:${SLOT}" \
		-b "${WORKDIR}" \
		-d "${prepstrip_sources_dir}" \
		-l "${tmpdir}/sources/${x##*/}.${BASHPID}" \
		"${x}")
}

# Try to create a symlink. Return success if it already exists.
__try_symlink() {
	local target=$1 name=$2

	# Check for an existing link before and after in case we are racing against
	# another process.
	[[ -L ${name} ]] ||
		ln -s "${target}" "${name}" ||
		[[ -L ${name} ]] ||
		die "failed to create symlink '${name}'"
}

# Usage: dedup_elf_debug <src> <inode_dedupdebug>
dedup_elf_debug() {
	# 1. File to dedup debug symbols
	# 2. Temp path for hard link tracking
	local src=$1 inode_dedupdebug=$2

	debug-print-function "${FUNCNAME}" "$@"

	# shellcheck disable=2317
	if (( ! has_feature[dedupdebug] || has_restriction[dedupdebug] )); then
		dedup_elf_debug() { :; }
		return
	elif ! hash "${name_of[dwz]}" 2>/dev/null; then
		stash_warning <<-'EOF'
		FEATURES=dedupdebug requires the dwz binary, which was not found!
		EOF
		dedup_elf_debug() { :; }
		return
	fi

	# We already dedupdebug-ed this inode.
	[[ -L ${inode_dedupdebug} ]] && return 0

	"${name_of[dwz]}" -- "${src}"
	touch "${inode_dedupdebug}"
}

# Usage: save_elf_debug <src> <inode_debug> [splitdebug]
save_elf_debug() {
	# 1. File from which we extract symbols.
	# 2. Temp path for hard link tracking
	# 3. Existing debug file optionally created by eu-strip in parent function
	local src=$1 inode_debug=$2 splitdebug=$3
	local {src,dst}_{buildid_rel,basename,dir} buildid_{file,dir} arg dst
	local -a objcopy_flags

	debug-print-function "${FUNCNAME}" "$@"

	# NOTE: Debug files must be installed in
	# ${EPREFIX}/usr/lib/debug/${EPREFIX} (note that ${EPREFIX} occurs
	# twice in this path) in order for gdb's debug-file-directory
	# lookup to work correctly.

	# Source paths
	src_basename=${src##*/}
	src_dirname=${src%/*}

	# Destination paths
	dst_dirname=${ED%/}/usr/lib/debug/${src_dirname#"${D%/}"/}

	# dont save debug info twice
	[[ ${src} == *".debug" ]] && return 0

	mkdir -p "${dst_dirname}" || die "failed to create directory '${dst_dirname}'"

	if [[ -L ${inode_debug} ]] ; then
		# We already created a debug file for this inode.
		# Read back the file name, and create another hard link if necessary.
		dst_basename=$(readlink "${inode_debug}") || die "failed to read link '${inode_debug}'"
		dst_basename=${dst_basename##*/}
		dst=${dst_dirname}/${dst_basename}
		if [[ ! -e ${dst} ]]; then
			debug-print "creating hard link: target: '${inode_debug}' name: '${dst}'"
			ln -L "${inode_debug}" "${dst}" || die "failed to create hard link '${dst}'"
		fi
	else
		dst_basename=${src_basename}.debug
		dst=${dst_dirname}/${dst_basename}
		if [[ -n ${splitdebug} ]] ; then
			mv "${splitdebug}" "${dst}"
		else
			objcopy_flags=( --only-keep-debug )
			if (( has_feature[compressdebug] )); then
				objcopy_flags+=( --compress-debug-sections )
			fi
			"${name_of[objcopy]}" "${objcopy_flags[@]}" "${src}" "${dst}" \
			&& "${name_of[objcopy]}" --add-gnu-debuglink="${dst}" "${src}"
		fi

		# Only do the following if the debug file was
		# successfully created (see bug #446774).
		# shellcheck disable=2181
		if [[ $? -eq 0 ]] ; then
			arg="a-x,o-w"
			[[ -g ${src} || -u ${src} ]] && arg+=",go-r"
			chmod "${arg}" "${dst}"

			# Symlink so we can read the name back.
			__try_symlink "${dst}" "${inode_debug}"

			# If we don't already have build-id from debugedit, look it up.
			# This should only happen with FEATURES=-installsources, as
			# it's done in save_elf_sources.
			if [[ -z ${buildid} ]] ; then
				if hash "${name_of[debugedit]}" 2>/dev/null; then
					# Salt the build ID to avoid collisions on
					# bundled libraries.
					buildid=$("${name_of[debugedit]}" -i \
						-s "${CATEGORY}/${PF}:${SLOT}" \
						"${x}")
				elif ! contains_word buildid "${warned_for[debugedit]}"; then
					warned_for[debugedit]+=" buildid"
					stash_warning <<-'EOF'
					FEATURES=splitdebug requires the debugedit binary, which was not found!
					This feature won't work properly with build IDs until debugedit is installed.
					EOF
				fi
			fi

			# If we (still) don't already have build-id from debugedit, look it up.
			if [[ -z ${buildid} ]] ; then
				# Convert the readelf output to something useful
				buildid=$("${name_of[readelf]}" -n "${src}" 2>/dev/null | awk '/Build ID:/{ print $NF; exit }')
			fi

			if [[ -n ${buildid} ]] ; then
				buildid_dir="${ED%/}/usr/lib/debug/.build-id/${buildid:0:2}"
				buildid_file="${buildid_dir}/${buildid:2}"
				src_buildid_rel="../../../../../${src#"${ED%/}"/}"
				dst_buildid_rel="../../${dst#"${ED%/}"/usr/lib/debug/}"
				mkdir -p "${buildid_dir}" || die
				__try_symlink "${dst_buildid_rel}" "${buildid_file}.debug"
				__try_symlink "${src_buildid_rel}" "${buildid_file}"
			fi
		fi
	fi
}

# Usage: process_elf <elf>
process_elf() {
	local x=$1 inode_link=$2
	local already_stripped lock{tries,file} splitdebug shortname xt_data
	shift 2

	__vecho "   ${x#"${ED%/}"}"

	# If two processes try to debugedit or strip the same hardlink at the
	# same time, it may corrupt files or cause loss of splitdebug info.
	# So, use a lockfile to prevent interference (easily observed with
	# dev-vcs/git which creates ~111 hardlinks to one file in
	# /usr/libexec/git-core).
	lockfile=${inode_link}_lockfile
	locktries=100
	while ! ln "${inode_link}" "${lockfile}" 2>/dev/null; do
		(( --locktries > 0 )) || die "failed to acquire lock '${lockfile}'"
		sleep 1
	done

	if [[ -f ${inode_link}_stripped ]]; then
		already_stripped=1
	else
		already_stripped=0
		if (( do_preserve_xattr )); then
			xt_data=$(dump_xattrs "${x}")
		fi
		save_elf_sources "${x}"
		dedup_elf_debug "${x}" "${inode_link}_dedupdebug"
	fi

	if (( do_strip )); then
		# See if we can split & strip at the same time
		if (( do_splitdebug )) && [[ ${SPLIT_STRIP_FLAGS} ]]; then
			shortname=${x##*/}.debug
			splitdebug=${tmpdir}/splitdebug/${shortname}.${BASHPID}

			if (( ! already_stripped )); then
				"${name_of[strip]}" "$@" -f "${splitdebug}" -F "${shortname}" "${x}"
			fi
			save_elf_debug "${x}" "${inode_link}_debug" "${splitdebug}"
		else
			if (( do_splitdebug )); then
				save_elf_debug "${x}" "${inode_link}_debug"
			fi
			if (( ! already_stripped )); then
				"${name_of[strip]}" "$@" "${x}"
			fi
		fi
	fi

	if (( already_stripped )); then
		rm -f "${x}" || die "rm failed unexpectedly"
		ln "${inode_link}_stripped" "${x}" || die "ln failed unexpectedly"
	else
		ln "${x}" "${inode_link}_stripped" || die "ln failed unexpectedly"
		if [[ ${xt_data} ]] ; then
			restore_xattrs <<< "${xt_data}"
		fi
	fi

	[[ -n ${lockfile} ]] && rm -f "${lockfile}"
}

# Usage: process_ar <ar archive>
process_ar() {
	local x=$1

	__vecho "   ${x#"${ED%/}"}"

	if (( do_strip )); then
		# If we have split debug enabled, then do not strip this.
		# There is no concept of splitdebug for objects not yet
		# linked in (only for finally linked ELFs), so we have to
		# retain the debug info in the archive itself.
		if (( ! do_splitdebug )); then
			"${name_of[strip]}" -g "${x}" && "${name_of[ranlib]}" "${x}"
		fi
	fi
}

if  [[ ${USERLAND} == BSD ]]; then
	get_inode_number() {
		stat -f '%i' "$1"
	}
else
	get_inode_number() {
		stat -c '%i' "$1"
	}
fi

if hash getfattr 2>/dev/null; then
	dump_xattrs() {
		getfattr -d -m - --absolute-names "$1"
	}
else
	dump_xattrs() {
		PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \
		"${PORTAGE_PYTHON:-/usr/bin/python}" \
		"${PORTAGE_BIN_PATH}/xattr-helper.py" --dump < <(echo -n "$1")
	}
fi

if hash setfattr 2>/dev/null; then
	restore_xattrs() {
		setfattr --restore=-
	}
else
	restore_xattrs() {
		PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \
		"${PORTAGE_PYTHON:-/usr/bin/python}" \
		"${PORTAGE_BIN_PATH}/xattr-helper.py" --restore
	}
fi

do_ignore() {
	local -a skip_dirs
	local skip

	for skip; do
		if [[ -d ${ED%/}/${skip#/} ]]; then
			skip_dirs+=( "${ED%/}/${skip#/}" )
		else
			rm -f "${ED%/}/${skip#/}.estrip" || die
		fi
	done

	if (( ${#skip_dirs[@]} )); then
		printf '%s\0' "${skip_dirs[@]}" | find0 -name '*.estrip' -delete || die
	fi
}

do_queue() {
	local needed_entry{,_file} {,find_}path
	local -a find_paths scanelf_results

	for path; do
		if [[ -e ${ED%/}/${path#/} ]]; then
			find_paths+=( "${ED%/}/${path#/}" )
		fi
	done

	(( ${#find_paths[@]} )) || return 0

	# We can avoid scanelf calls for binaries we already
	# checked in install_qa_check (where we generate
	# NEEDED for everything installed).
	#
	# EAPI 7+ has controlled stripping (dostrip) though
	# which is why estrip has the queue/dequeue logic,
	# so we need to take the intersection of:
	# 1. files scanned earlier (all ELF installed)
	#    (note that this should be a superset of 2., so we don't
	#    need to worry about unknown files appearing)
	#
	# 2. the files we're interested in right now
	if [[ -f "${PORTAGE_BUILDDIR}"/build-info/NEEDED ]] ; then
		# The arguments may not be exact files (probably aren't), but search paths/directories
		# which should then be searched recursively.
		while IFS= read -r needed_entry ; do
			for find_path in "${find_paths[@]}" ; do
				# NEEDED has a bunch of entries like:
				# /usr/lib64/libfoo.so libc.so
				#
				# find_path entries may be exact paths (like /usr/lib64/libfoo.so)
				# or instead /usr/lib64, or ${ED}/usr, etc.
				#
				# We check if the beginning (i.e. first entry) of the NEEDED line
				# matches the path given
				# e.g. find_path="/usr/lib64" will match needed_entry="/usr/lib64/libfoo.so libc.so".
				needed_entry_file="${needed_entry% *}"
				if [[ ${needed_entry_file} == "${find_path#"${D}"}"* ]]; then
					scanelf_results+=( "${D}${needed_entry_file}" )
				fi
			done
		done < "${PORTAGE_BUILDDIR}"/build-info/NEEDED
	else
		mapfile -t scanelf_results < <(scanelf -yqRBF '#k%F' -k '.symtab' "${find_paths[@]}")
	fi

	while IFS= read -r path; do
		: >> "${path}.estrip" || die
	done < <(
		(( ${#scanelf_results[@]} )) && printf "%s\n" "${scanelf_results[@]}"
		printf '%s\0' "${find_paths[@]}" | find0 -type f ! -type l -name '*.a'
	)
}

declare -A has_feature
declare -A has_restriction

for key in compressdebug dedupdebug installsources nostrip splitdebug xattr; do
	contains_word "$key" "${FEATURES}"
	has_feature[$key]=$(( $? == 0 ))
done

for key in binchecks dedupdebug installsources splitdebug strip; do
	contains_word "$key" "${PORTAGE_RESTRICT}"
	has_restriction[$key]=$(( $? == 0 ))
done

if ! ___eapi_has_prefix_variables; then
	EPREFIX= ED=${D}
fi

if (( ! has_restriction[strip] && ! has_feature[nostrip] )); then
	do_banner=1
	do_skip=0
elif (( ! has_feature[installsources] )); then
	exit 0
else
	do_banner=0
	do_skip=1
fi

do_prepstrip=0

while [[ $# -gt 0 ]] ; do
	case $1 in
	--ignore)
		shift
		do_ignore "$@"
		exit
		;;
	--queue)
		shift
		do_queue "$@"
		exit
		;;
	--dequeue)
		[[ $# -eq 1 ]] || die "${0##*/}: $1 takes no additional arguments"
		break
		;;
	--prepallstrip)
		[[ $# -eq 1 ]] || die "${0##*/}: $1 takes no additional arguments"
		do_prepstrip=1
		break
		;;
	*)
		die "${0##*/}: unknown arguments '$*'"
		exit 1
		;;
	esac
	shift
done
set -- "${ED}"

[[ ${KERNEL} == linux ]] && (( has_feature[xattr] ))
do_preserve_xattr=$(( $? == 0 ))

# Determine the names of the tools that might subsequently be used. For several
# of these, their ${CHOST}-prefixed variants are preferred, if found to exist.
declare -A name_of
for bin in debugedit dwz {,"${CHOST}-"}{'objcopy','ranlib','readelf','strip'}; do
	key=${bin#"${CHOST}-"}
	if [[ ! ${name_of[$key]} ]] || hash "${bin}" 2>/dev/null; then
		name_of[$key]=${bin}
	fi
done

# If debugedit does not exist, consider some alternative locations for it.
if ! hash "${name_of[debugedit]}" 2>/dev/null; then
	debugedit_paths=(
		"${EPREFIX}/usr/libexec/rpm/debugedit"
	)
	for x in "${debugedit_paths[@]}"; do
		if [[ -x ${x} ]]; then
			name_of[debugedit]=${x}
			break
		fi
	done
fi

# Declare a map to keep track of whether warnings in certain categories have
# been issued for a missing tool.
declare -A warned_for

# Figure out what tool set we're using to strip stuff
unset SAFE_STRIP_FLAGS DEF_STRIP_FLAGS SPLIT_STRIP_FLAGS
case $("${name_of[strip]}" --version 2>/dev/null) in
	*elfutils*) # dev-libs/elfutils
		# elfutils default behavior is always safe, so don't need to specify
		# any flags at all
		SAFE_STRIP_FLAGS=""
		DEF_STRIP_FLAGS="--remove-comment"
		SPLIT_STRIP_FLAGS="-f"
		;;
	*GNU*) # sys-devel/binutils
		# We'll leave out -R .note for now until we can check out the relevance
		# of the section when it has the ALLOC flag set on it ...
		SAFE_STRIP_FLAGS="--strip-unneeded -N __gentoo_check_ldflags__"
		DEF_STRIP_FLAGS="-R .comment -R .GCC.command.line -R .note.gnu.gold-version"
		SPLIT_STRIP_FLAGS=
esac

read -rd '' -a portage_strip_flags <<<"${PORTAGE_STRIP_FLAGS-${SAFE_STRIP_FLAGS} ${DEF_STRIP_FLAGS}}"

prepstrip_sources_dir=${EPREFIX}/usr/src/debug/${CATEGORY}/${PF}

__multijob_init

# Create a temporary directory whose subsequent removal is guaranteed.
tmpdir=
trap 'rm -rf -- "${tmpdir}"' EXIT
tmpdir=$(mktemp -d -- "${T:-/tmp}/prepstrip.XXXXXX") || exit

# Set up a temporary directory structure that we care about.
mkdir -p "${tmpdir}"/{inodes,splitdebug,sources}

# The existance of the section .symtab tells us that a binary is stripped.
# We want to log already stripped binaries, as this may be a QA violation.
# They prevent us from getting the splitdebug data.
if (( ! has_restriction[binchecks] )); then
	# We need to do the non-stripped scan serially first before we turn around
	# and start stripping the files ourselves.  The log parsing can be done in
	# parallel though.
	log=${tmpdir}/scanelf-already-stripped.log
	scanelf -yqRBF '#k%F' -k '!.symtab' "$@" | sed -e "s#^${ED%/}/##" > "${log}"

	(
	__multijob_child_init
	qa_var="QA_PRESTRIPPED_${ARCH/-/_}"
	[[ -n ${!qa_var} ]] && QA_PRESTRIPPED="${!qa_var}"
	if [[ -n ${QA_PRESTRIPPED} && -s ${log} &&
		! -v QA_STRICT_PRESTRIPPED ]] ; then
		shopts=$-
		set -o noglob
		for x in ${QA_PRESTRIPPED} ; do
			sed -e "s#^${x#/}\$##" -i "${log}"
		done
		set +o noglob
		set -${shopts}
	fi
	sed -e "/^\$/d" -e "s#^#/#" -i "${log}"
	if [[ -s ${log} ]] ; then
		__vecho -e "\n"
		eqawarn "QA Notice: Pre-stripped files found:"
		eqawarn "$(<"${log}")"
	else
		rm -f "${log}"
	fi
	) &
	__multijob_post_fork
fi

cd "${tmpdir}/inodes" || die "cd failed unexpectedly"

# Since strip creates a new inode, we need to know the initial set of inodes in
# advance, so that we can avoid interference due to trying to strip the same
# (hardlinked) file multiple times in parallel. See bug #421099.
if (( do_prepstrip )); then
	while IFS= read -r x; do
		inode_link=$(get_inode_number "${x}") || die "stat failed unexpectedly"
		echo "${x}" >> "${inode_link}" || die "echo failed unexpectedly"
	done < <(
		# NEEDED may not exist for some packages (bug #862606)
		if [[ -f "${PORTAGE_BUILDDIR}"/build-info/NEEDED ]] ; then
			while IFS= read -r needed_entry ; do
				needed_entry="${needed_entry% *}"
				needed_contents+=( "${D%/}${needed_entry}" )
			done < "${PORTAGE_BUILDDIR}"/build-info/NEEDED
		fi

		# Use sort -u to eliminate duplicates (bug #445336).
		(
			if (( ${#needed_contents[@]} )); then
				printf "%s\n" "${needed_contents[@]}"
			fi
			printf '%s\0' "$@" | find0 -type f ! -type l -name '*.a'
		) | LC_ALL=C sort -u
	)
else
	while IFS= read -d '' -r x ; do
		inode_link=$(get_inode_number "${x%.estrip}") || die "stat failed unexpectedly"
		echo "${x%.estrip}" >> "${inode_link}" || die "echo failed unexpectedly"
	done < <(printf '%s\0' "${ED}" | find0 -name '*.estrip' -delete -print0)
fi

# Now we look for unstripped binaries.
read -rd '' -a safe_strip_flags <<<"${SAFE_STRIP_FLAGS}"
for inode_link in *; do
	test -e "${inode_link}" || continue
	# shellcheck disable=2094
	while IFS= read -r x; do
		if (( do_banner )); then
			__vecho "strip: ${name_of[strip]} ${portage_strip_flags[*]}"
			do_banner=0
		fi

		(
		__multijob_child_init
		f=$(file -S "${x}") || exit 0
		[[ -z ${f} ]] && exit 0

		if (( do_skip )); then
			do_strip=0
		elif (( ! do_prepstrip )); then
			do_strip=1
		else
			do_strip=1
			while IFS= read -rd '' glob; do
				# shellcheck disable=2053
				if [[ ${x#"${ED%/}"} == ${glob} ]]; then
					do_strip=0
					break
				fi
			done < <(
				# FIXME: This facilitates code injection, just
				# for the sake of supporting brace-expansion.
				# To support it amounts to a grave anti-feature.
				# It really ought to be dropped.
				shopt -o -s noglob
				eval "printf '%s\\0' ${STRIP_MASK}"
			)
		fi

		if (( has_feature[splitdebug] && ! has_restriction[splitdebug] )); then
			do_splitdebug=1
		else
			do_splitdebug=0
		fi

		# In Prefix we are usually an unprivileged user, so we can't strip
		# unwritable objects.  Make them temporarily writable for the
		# stripping.
		was_writable=1
		if [[ ! -w ${x} ]] ; then
			was_writable=0
			chmod u+w "${x}"
		fi

		# only split debug info for final linked objects
		# or kernel modules as debuginfo for intermediatary
		# files (think crt*.o from gcc/glibc) is useless and
		# actually causes problems.  install sources for all
		# elf types though because that stuff is good.

		buildid=
		if [[ ${f} == *"current ar archive"* ]] ; then
			process_ar "${x}"
		elif [[ ${f} == *SB\ @(?(pie )executable|shared object)* ]] ; then
			process_elf "${x}" "${inode_link}" "${portage_strip_flags[@]}"
		elif [[ ${f} == *"SB relocatable"* ]] ; then
			[[ ${x} == *.ko ]] || do_splitdebug=0
			process_elf "${x}" "${inode_link}" "${safe_strip_flags[@]}"
		fi

		if (( ! was_writable )); then
			chmod u-w "${x}"
		fi
		) &
		__multijob_post_fork

	done < "${inode_link}"
done

# With a bit more work, we could run the rsync processes below in
# parallel, but not sure that'd be an overall improvement.
__multijob_finish

cd "${tmpdir}"/sources/ && cat -- * > "${tmpdir}/debug.sources" 2>/dev/null
if [[ -s ${tmpdir}/debug.sources ]] \
	&& (( has_feature[installsources] && ! has_restriction[installsources] )) \
	&& hash "${name_of[debugedit]}" 2>/dev/null
then
	__vecho "installsources: rsyncing source files"
	[[ -d ${D%/}/${prepstrip_sources_dir#/} ]] || mkdir -p "${D%/}/${prepstrip_sources_dir#/}"

	# Skip installation of ".../<foo>" (system headers? why inner slashes are forbidden?)
	# Skip syncing of ".../foo/" (complete directories)
	grep -zv -e '/<[^/>]*>$' -e '/$' "${tmpdir}"/debug.sources | {
		cd "${WORKDIR}" || exit
		LANG=C sort -z -u \
		| rsync -tL0 --chmod=ugo-st,a+r,go-w,Da+x,Fa-x --files-from=- "${WORKDIR}/" "${D%/}/${prepstrip_sources_dir#/}/"
	}

	# Preserve directory structure.
	# Needed after running save_elf_sources.
	# https://bugzilla.redhat.com/show_bug.cgi?id=444310
	while IFS= read -rd '' emptydir; do
		: >> "${emptydir}"/.keepdir
	done < <(printf '%s\0' "${D%/}/${prepstrip_sources_dir#/}/" | find0 -type d -empty -print0)
fi

# Collect any outstanding warnings and convey them through ewarn.
raise_warnings
