#!/bin/sh -e

# generate debug symbol package for a given binary package within a built
# debian source tree.
#
# Author: Martin Pitt <martin.pitt@ubuntu.com>
# (C) 2006 Canonical Ltd.
#
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

# evaluate options
blacklist=""
add_to_files=0

DEB_HOST_GNU_TYPE=$(dpkg-architecture -qDEB_HOST_GNU_TYPE)
DEB_BUILD_GNU_TYPE=$(dpkg-architecture -qDEB_BUILD_GNU_TYPE)
if [ "$DEB_HOST_GNU_TYPE" = "$DEB_BUILD_GNU_TYPE" ]; then
	READELF=readelf
	OBJCOPY=objcopy
else
	READELF=${DEB_HOST_GNU_TYPE}-readelf
	OBJCOPY=${DEB_HOST_GNU_TYPE}-objcopy
fi

dbg() {
    echo "$0: $*"
}

for p; do
    case $p in
        -X*)
            blacklist=`/bin/echo -e "${p#-X}\n$blacklist"`
            shift
            ;;
        -a)
            add_to_files=1
            shift
            ;;
        *)
            break
            ;;
    esac
done

pkgname="$1"
pkgpath="$2"
nodebuglink="$3"

[ -n "$pkgpath" ] || pkgpath="debian/$pkgname"

[ -d "$pkgpath" ] || {
    if [ -z "$pkgname" ]; then
	echo "Usage: $0 [-Xitem] [-n] <binary package name> [<package path>] [nodebuglink]"
    else
	echo "Directory $pkgpath does not exist, aborting" >&2
    fi
    exit 1
}

# determine set of -dbg packages; if there are any, we just build dummy dbgsyms
# that depend on -dbg, otherwise we depend on the actual package; but ignore
# python-*-dbg, these are quite different
dbgdepends=
for p in `grep '^Package:.*-dbg' debian/control | cut -f 2 -d\ `; do
    if [ "$p" != "${p#python}" ]; then
        dbg "ignoring python* package: $p"
        continue
    fi
    # heuristic to avoid considering transitional -dbg packages which are going
    # to be empty
    if [ -n "`perl -n000 -e 'print if m/Package: $p/ && m/transitional/' debian/control`" ]; then
        dbg "ignoring transitional package $p"
        continue
    fi
    [ -z "$dbgdepends" ] || dbgdepends="$dbgdepends, "
    dbgdepends="$dbgdepends$p (= \1)"
done
if [ -n "$dbgdepends" ]; then
    depends="$dbgdepends"
else
    depends="$pkgname (= \1)"
fi

p=`readlink -f "$pkgpath"` # original package path
dp="debian/${pkgname}-dbgsym" # debug package path
dp=`readlink -f "$dp"` # debug package path

if [ -e "debian/compat" ] && [ "`cat debian/compat`" -ge 9 ]; then
    use_buildid="1"
    echo "Using buildid for compat level >= 9"
fi

# find all ELF files and extract their debug symbols; also add debug links
# to original files; if we do not find any unstripped files, then a likely
# cause is that we already ran this program on the current package, in which
# case we must not run it again to not destroy debug link CRC sums and produce
# empty ddebs
unset any_unstripped
origdir=`pwd`
cd $p
find -type f | ( while read f; do
    # ignore static libraries
    if echo "$f" | grep -q '\.[ao]$'; then continue; fi
    # ignore non-ELF files
    $READELF -h "$f" > /dev/null 2>&1 || continue
    # check blacklist
    if [ -n "$blacklist" ]; then
        if echo "$f" | fgrep -q "$blacklist"; then continue; fi
    fi
    if ! $READELF -S "$f" | grep -q ' .debug_'; then
	# no debug information
	continue
    fi
    any_unstripped=1

    if [ -n "$dbgdepends" ]; then
        echo "we have -dbg package(s), not installing anything into ddeb" >&2
        exit 0
    fi

    # use buildid for compat >= 9, and when present
    dest=`LANGUAGE= LC_ALL=C readelf -n "$f" | awk '/Build.ID:/ { print ".build-id/" substr($3, 1,2) "/" substr($3,3) ".debug" }' | head -n1`
    if [ -z "$use_buildid" -o -z "$dest" ]; then
        #echo "using debuglink for $f: $dest"
        dest="$f"
    fi

    mkdir -p $dp/usr/lib/debug/`dirname "$dest"`
    $OBJCOPY --only-keep-debug "$f" "$dp/usr/lib/debug/$dest" || {
	echo "objcopy --only-keep-debug failed on `readlink -f $f`" >&2
	exit 1
    }	
    [ -n "$nodebuglink" ] || $OBJCOPY -R .gnu_debuglink --add-gnu-debuglink="$dp/usr/lib/debug/$dest" "$f" || {
	echo "objcopy --add-gnu-debuglink failed on `readlink -f $f`" >&2
	exit 1
    }
done
if [ -z "$any_unstripped" ]; then
    echo "$pkgname has no unstripped objects, ignoring" >&2
    rm -rf $dp
    exit 0
fi
)
cd $origdir

if [ ! -d "$dp" -o -z "`find $dp -type f`" ] && [ -z "$dbgdepends" ]; then
    dbg "nothing in $dp and no dbgdepends, ignoring"
    rm -rf $dp
    exit 0
fi

mkdir -p $dp/DEBIAN

# generate a control file for current package (since it is usually not yet
# existing) and adapt it to be suitable for the dbgsym package
perl -p000 -e 's/^#.*?\n//msg; s/^(?:Pre-Depends|Depends|Recommends|Suggests|Conflicts|Provides|Replaces|Breaks|Enhances|Essential):.*?\n(?![ \t])//msg' debian/control |
    dpkg-gencontrol -c- -fdhstrip-dummy-debian-files -p$pkgname -is -ip -Pdebian/$pkgname-dbgsym -Tdebian/$pkgname.substvars -O | 
    sed "s/^Priority:.*$/Priority: extra/;
    s/^Version: \(.*\)$/&\nDepends: $depends/;
    s/^Package:.*$/&-dbgsym/;
    s/^Description:.*$/Description: debug symbols for package $pkgname/" \
    > $dp/DEBIAN/control
rm -f dhstrip-dummy-debian-files

# binaries named the same as the source will lack a Source field.
# since we are renaming binaries, we might need to add one.
if [ -z "`grep ^Source: $dp/DEBIAN/control`" ]; then
    sed -i "s/^Package:.*$/&\nSource: $pkgname/" $dp/DEBIAN/control
fi

# extract package name, version, architecture
ddebname=${pkgname}-dbgsym
ddebversion=`grep '^Version:' $dp/DEBIAN/control | cut -f2- -d\ `
ddebversion=${ddebversion#*:} # strip off epoch
ddebarch=`grep '^Architecture:' $dp/DEBIAN/control | cut -f2- -d\ `
ddebsection=`grep '^Section:' $dp/DEBIAN/control | cut -f2- -d\ `
ddebpriority=`grep '^Priority:' $dp/DEBIAN/control | cut -f2- -d\ `

[ -n "$ddebsection" -a -n "$ddebpriority" ] || {
    echo "Error: parsed ddeb section or priority is empty" >&2
    exit 1
}
ddeb="${ddebname}_${ddebversion}_${ddebarch}.ddeb"

# build .ddeb and add it to debian/files
dbg "building ddeb package"
NO_PKG_MANGLE=1 dpkg-deb -Zxz --build $dp ../$ddeb
if [ "$add_to_files" = "1" ]; then
    dbg "dpkg-distaddfile $ddeb $ddebsection $ddebpriority"
    dpkg-distaddfile "$ddeb" "$ddebsection" "$ddebpriority"
fi

# clean up, since this package does not appear in debian/control
rm -rf $dp
