#!/bin/bash
# Copyright 1999-2007 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id: prepstrip 14707 2009-10-23 18:20:34Z zmedico $

source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh

banner=false
SKIP_STRIP=false
if hasq nostrip ${FEATURES} || \
   hasq strip ${RESTRICT}
then
	SKIP_STRIP=true
	banner=true
	hasq installsources ${FEATURES} || exit 0
fi

STRIP=${STRIP:-${CHOST}-strip}
type -P -- ${STRIP} > /dev/null || STRIP=strip
OBJCOPY=${OBJCOPY:-${CHOST}-objcopy}
type -P -- ${OBJCOPY} > /dev/null || OBJCOPY=objcopy

# 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 ...
export SAFE_STRIP_FLAGS="--strip-unneeded"
export PORTAGE_STRIP_FLAGS=${PORTAGE_STRIP_FLAGS-${SAFE_STRIP_FLAGS} -R .comment}
prepstrip_sources_dir=/usr/src/debug/${CATEGORY}/${PF}

if hasq installsources ${FEATURES} && ! type -P debugedit >/dev/null ; then
	ewarn "FEATURES=installsources is enabled but the debugedit binary could not"
	ewarn "be found. This feature will not work unless debugedit is installed!"
fi

unset ${!INODE_*}

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

save_elf_sources() {
	hasq installsources ${FEATURES} || return 0
	hasq installsources ${RESTRICT} && return 0
	type -P debugedit >/dev/null || return 0

	local x=$1
	local inode=$(inode_var_name "$x")
	[[ -n ${!inode} ]] && return 0
	debugedit -b "${WORKDIR}" -d "${prepstrip_sources_dir}" \
		-l "${T}"/debug.sources "${x}"
}

save_elf_debug() {
	hasq splitdebug ${FEATURES} || return 0

	local x=$1
	local y="${D}usr/lib/debug/${x:${#D}}.debug"

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

	# this will recompute the build-id, but for now that's ok
	local buildid="$( type -P debugedit >/dev/null && debugedit -i "${x}" )"

	mkdir -p $(dirname "${y}")

	local inode=$(inode_var_name "$x")
	if [[ -n ${!inode} ]] ; then
		ln "${D}usr/lib/debug/${!inode:${#D}}.debug" "$y"
	else
		eval $inode=\$x
		${OBJCOPY} --only-keep-debug "${x}" "${y}"
		${OBJCOPY} --add-gnu-debuglink="${y}" "${x}"
		[[ -g ${x} ]] && chmod go-r "${y}"
		[[ -u ${x} ]] && chmod go-r "${y}"
		chmod a-x,o-w "${y}"
	fi

	if [[ -n ${buildid} ]] ; then
		local buildid_dir="${D}usr/lib/debug/.build-id/${buildid:0:2}"
		local buildid_file="${buildid_dir}/${buildid:2}"
		mkdir -p "${buildid_dir}"
		ln -s "../../${x:${#D}}.debug" "${buildid_file}.debug"
		ln -s "/${x:${#D}}" "${buildid_file}"
	fi
}

# 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 ! hasq binchecks ${RESTRICT} && \
	! hasq strip ${RESTRICT} ; then
	log=$T/scanelf-already-stripped.log
	qa_var="QA_PRESTRIPPED_${ARCH/-/_}"
	[[ -n ${!qa_var} ]] && QA_PRESTRIPPED="${!qa_var}"
	scanelf -yqRBF '#k%F' -k '!.symtab' "$@" | sed -e "s#^$D##" > "$log"
	if [[ -n $QA_PRESTRIPPED && -s $log && \
		${QA_STRICT_PRESTRIPPED-unset} = unset ]] ; 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 "\a\n"
		eqawarn "QA Notice: Pre-stripped files found:"
		eqawarn "$(<"$log")"
	else
		rm -f "$log"
	fi
fi

# Now we look for unstripped binaries.
for x in \
	$(scanelf -yqRBF '#k%F' -k '.symtab' "$@") \
	$(find "$@" -type f -name '*.a')
do
	if ! ${banner} ; then
		vecho "strip: ${STRIP} ${PORTAGE_STRIP_FLAGS}"
		banner=true
	fi

	f=$(file "${x}") || continue
	[[ -z ${f} ]] && continue

	if ! ${SKIP_STRIP} ; then
		# The noglob funk is to support STRIP_MASK="/*/booga" and to keep
		#  the for loop from expanding the globs.
		# The eval echo is to support STRIP_MASK="/*/{booga,bar}" sex.
		set -o noglob
		strip_this=true
		for m in $(eval echo ${STRIP_MASK}) ; do
			[[ /${x#${D}} == ${m} ]] && strip_this=false && break
		done
		set +o noglob
	else
		strip_this=false
	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 cause that stuff is good.

	if [[ ${f} == *"current ar archive"* ]] ; then
		vecho "   ${x:${#D}}"
		if ${strip_this} ; then
			# hmm, can we split debug/sources for .a ?
			${STRIP} -g "${x}"
		fi
	elif [[ ${f} == *"SB executable"* || ${f} == *"SB shared object"* ]] ; then
		vecho "   ${x:${#D}}"
		save_elf_sources "${x}"
		if ${strip_this} ; then
			save_elf_debug "${x}"
			${STRIP} ${PORTAGE_STRIP_FLAGS} "${x}"
		fi
	elif [[ ${f} == *"SB relocatable"* ]] ; then
		vecho "   ${x:${#D}}"
		save_elf_sources "${x}"
		if ${strip_this} ; then
			[[ ${x} == *.ko ]] && save_elf_debug "${x}"
			${STRIP} ${SAFE_STRIP_FLAGS} "${x}"
		fi
	fi
done

if [[ -s ${T}/debug.sources ]] && \
        hasq installsources ${FEATURES} && \
        ! hasq installsources ${RESTRICT} && \
        type -P debugedit >/dev/null
then
	vecho "installsources: rsyncing source files"
	[[ -d ${D}${prepstrip_sources_dir} ]] || mkdir -p "${D}${prepstrip_sources_dir}"
	grep -zv '/<[^/>]*>$' "${T}"/debug.sources | \
		(cd "${WORKDIR}"; LANG=C sort -z -u | \
		rsync -tL0 --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 read -r -d $'\0' emptydir
	do
		touch "$emptydir"/.keepdir
	done < <(find "${D}${prepstrip_sources_dir}/" -type d -empty -print0)
fi
