#!/bin/sh
#
# builddeb 1.3
# Copyright 2003 Wichert Akkerman <wichert@wiggy.net>
#
# Simple script to generate a deb package for a Linux kernel. All the
# complexity of what to do with a kernel after it is installed or removed
# is left to other scripts and packages: they can install scripts in the
# /etc/kernel/{pre,post}{inst,rm}.d/ directories (or an alternative location
# specified in KDEB_HOOKDIR) that will be called on package install and
# removal.

set -e

is_enabled() {
	grep -q "^$1=y" include/config/auto.conf
}

if_enabled_echo() {
	if is_enabled "$1"; then
		echo -n "$2"
	elif [ $# -ge 3 ]; then
		echo -n "$3"
	fi
}

create_package() {
	local pname="$1" pdir="$2"

	mkdir -m 755 -p "$pdir/DEBIAN"
	mkdir -p "$pdir/usr/share/doc/$pname"
	cp debian/copyright "$pdir/usr/share/doc/$pname/"
	cp debian/changelog "$pdir/usr/share/doc/$pname/changelog.Debian"
	gzip -9 "$pdir/usr/share/doc/$pname/changelog.Debian"
	sh -c "cd '$pdir'; find . -type f ! -path './DEBIAN/*' -printf '%P\0' \
		| xargs -r0 md5sum > DEBIAN/md5sums"

	# Fix ownership and permissions
	chown -R root:root "$pdir"
	chmod -R go-w "$pdir"
	# in case we are in a restrictive umask environment like 0077
	chmod -R a+rX "$pdir"

	# Create preinstall and post install script to remove dtb
	if [ "$3" = "dtb" ]; then
		echo "if [ -d /boot/dtb-$version ]; then mv /boot/dtb-$version /boot/dtb-$version.old; fi" >> $pdir/DEBIAN/preinst
		echo "if [ -d /boot/dtb.old ]; then rm -rf /boot/dtb.old; fi" >> $pdir/DEBIAN/preinst
		echo "if [ -d /boot/dtb ]; then mv /boot/dtb /boot/dtb.old; fi" >> $pdir/DEBIAN/preinst
		echo "exit 0" >> $pdir/DEBIAN/preinst
		chmod 775 $pdir/DEBIAN/preinst

		echo "if [ -d /boot/dtb-$version.old ]; then rm -rf /boot/dtb-$version.old; fi" >> $pdir/DEBIAN/postinst
		echo "ln -sf dtb-$version /boot/dtb > /dev/null 2>&1 || mv /boot/dtb-$version /boot/dtb" >> $pdir/DEBIAN/postinst
		echo "exit 0" >> $pdir/DEBIAN/postinst
		chmod 775 $pdir/DEBIAN/postinst
	fi

	# Create postinstall script for headers
	if [ "$3" = "headers" ]; then
		echo "cd /usr/src/linux-headers-$version; echo \"Compiling headers - please wait ...\"; find -type f -exec touch {} +;make -s scripts >/dev/null; make -s M=scripts/mod/ >/dev/null" >> $pdir/DEBIAN/postinst
		echo "exit 0" >> $pdir/DEBIAN/postinst
		chmod 775 $pdir/DEBIAN/postinst
	fi

	# Create the package
	dpkg-gencontrol -p$pname -P"$pdir"
	dpkg --build "$pdir" ..
}

set_debarch() {
	# Attempt to find the correct Debian architecture
	case "$UTS_MACHINE" in
	i386|ia64|alpha)
		debarch="$UTS_MACHINE" ;;
	x86_64)
		debarch=amd64 ;;
	sparc*)
		debarch=sparc ;;
	s390*)
		debarch=s390$(grep -q CONFIG_64BIT=y $KCONFIG_CONFIG && echo x || true) ;;
	ppc*)
		debarch=$(grep -q CPU_LITTLE_ENDIAN=y $KCONFIG_CONFIG && echo ppc64el || echo powerpc) ;;
	parisc*)
		debarch=hppa ;;
	mips*)
		debarch=mips$(grep -q CPU_LITTLE_ENDIAN=y $KCONFIG_CONFIG && echo el || true) ;;
	aarch64|arm64)
		debarch=arm64 ;;
	arm*)
		if grep -q CONFIG_AEABI=y $KCONFIG_CONFIG; then
		    if grep -q CONFIG_VFP=y $KCONFIG_CONFIG; then
			debarch=armhf
		    else
			debarch=armel
		    fi
		else
		    debarch=arm
		fi
		;;
	*)
		debarch=$(dpkg --print-architecture)
		echo "" >&2
		echo "** ** **  WARNING  ** ** **" >&2
		echo "" >&2
		echo "Your architecture doesn't have its equivalent" >&2
		echo "Debian userspace architecture defined!" >&2
		echo "Falling back to using your current userspace instead!" >&2
		echo "Please add support for $UTS_MACHINE to ${0} ..." >&2
		echo "" >&2
	esac
	if [ -n "$KBUILD_DEBARCH" ] ; then
		debarch="$KBUILD_DEBARCH"
	fi
	forcearch="-DArchitecture=$debarch"

}

# Some variables and settings used throughout the script
version=$KERNELRELEASE
revision=$(cat .version)
if [ -n "$KDEB_PKGVERSION" ]; then
	packageversion=$KDEB_PKGVERSION
else
	packageversion=$version-$revision
fi
sourcename=$KDEB_SOURCENAME
tmpdir="$objtree/debian/tmp"
kernel_headers_dir="$objtree/debian/hdrtmp"
libc_headers_dir="$objtree/debian/headertmp"
dbg_dir="$objtree/debian/dbgtmp"
packagename=linux-image-current"$LOCALVERSION"
kernel_headers_packagename=linux-headers-current"$LOCALVERSION"
libc_headers_packagename=linux-libc-dev
dbg_packagename=$packagename-dbg
debarch=
forcearch=
set_debarch

if [ "$ARCH" = "um" ] ; then
	packagename=user-mode-linux-$version
fi

# Not all arches have the same installed path in debian
# XXX: have each arch Makefile export a variable of the canonical image install
# path instead
case $ARCH in
aarch64|arm64)
	image_name=Image
	installed_image_path="boot/vmlinuz-$version"

	;;
arm*)
	image_name=zImage
	installed_image_path="boot/vmlinuz-$version"
	;;
um)
	installed_image_path="usr/bin/linux-$version"
	;;
parisc|mips|powerpc)
	installed_image_path="boot/vmlinux-$version"
	;;
*)
	installed_image_path="boot/vmlinuz-$version"
esac

BUILD_DEBUG=$(if_enabled_echo CONFIG_DEBUG_INFO Yes)

# Setup the directory structure
rm -rf "$tmpdir" "$kernel_headers_dir" "$libc_headers_dir" "$dbg_dir" "$dtb_dir" $objtree/debian/files
mkdir -m 755 -p "$dtb_dir/DEBIAN"
mkdir -p "$dtb_dir/boot/dtb-$version" "$dtb_dir/usr/share/doc/$dtb_packagename"
mkdir -m 755 -p "$tmpdir/DEBIAN"
mkdir -p "$tmpdir/lib" "$tmpdir/boot"
mkdir -p "$kernel_headers_dir/lib/modules/$version/"

# Build and install the kernel
if [ "$ARCH" = "um" ] ; then
	mkdir -p "$tmpdir/usr/lib/uml/modules/$version" "$tmpdir/usr/bin" "$tmpdir/usr/share/doc/$packagename"
	$MAKE linux
	cp System.map "$tmpdir/usr/lib/uml/modules/$version/System.map"
	cp $KCONFIG_CONFIG "$tmpdir/usr/share/doc/$packagename/config"
	gzip "$tmpdir/usr/share/doc/$packagename/config"
else
	cp System.map "$tmpdir/boot/System.map-$version"
	cp $KCONFIG_CONFIG "$tmpdir/boot/config-$version"
fi
cp arch/$ARCH/boot/Image "$tmpdir/$installed_image_path"

if is_enabled CONFIG_OF_EARLY_FLATTREE; then
	# Only some architectures with OF support have this target
	if [ -d "${srctree}/arch/$SRCARCH/boot/dts" ]; then
		$MAKE -f $srctree/Makefile INSTALL_DTBS_PATH="$tmpdir/boot/dtb" dtbs_install
	fi
fi

if is_enabled CONFIG_MODULES; then
	INSTALL_MOD_PATH="$tmpdir" $MAKE -f $srctree/Makefile modules_install
	rm -f "$tmpdir/lib/modules/$version/build"
	rm -f "$tmpdir/lib/modules/$version/source"
	if [ "$ARCH" = "um" ] ; then
		mv "$tmpdir/lib/modules/$version"/* "$tmpdir/usr/lib/uml/modules/$version/"
		rmdir "$tmpdir/lib/modules/$version"
	fi
	if [ -n "$BUILD_DEBUG" ] ; then
		for module in $(find $tmpdir/lib/modules/ -name *.ko -printf '%P\n'); do
			module=lib/modules/$module
			mkdir -p $(dirname $dbg_dir/usr/lib/debug/$module)
			# only keep debug symbols in the debug file
			$OBJCOPY --only-keep-debug $tmpdir/$module $dbg_dir/usr/lib/debug/$module
			# strip original module from debug symbols
			$OBJCOPY --strip-debug $tmpdir/$module
			# then add a link to those
			$OBJCOPY --add-gnu-debuglink=$dbg_dir/usr/lib/debug/$module $tmpdir/$module
		done

		# resign stripped modules
		if is_enabled CONFIG_MODULE_SIG_ALL; then
			INSTALL_MOD_PATH="$tmpdir" $MAKE -f $srctree/Makefile modules_sign
		fi
	fi
fi

if grep -q '^CONFIG_OF=y' $KCONFIG_CONFIG ; then
	#mkdir -p "$tmpdir/boot/dtb"
	INSTALL_DTBS_PATH="$dtb_dir/boot/dtb-$version" $MAKE KBUILD_SRC= dtbs_install
fi

if [ "$ARCH" != "um" ]; then
	$MAKE -f $srctree/Makefile headers
	$MAKE -f $srctree/Makefile headers_install INSTALL_HDR_PATH="$libc_headers_dir/usr"
	# move asm headers to /usr/include/<libc-machine>/asm to match the structure
	# used by Debian-based distros (to support multi-arch)
	host_arch=$(dpkg-architecture -a$(cat debian/arch) -qDEB_HOST_MULTIARCH)
	mkdir $libc_headers_dir/usr/include/$host_arch
	mv $libc_headers_dir/usr/include/asm $libc_headers_dir/usr/include/$host_arch/
fi

# Install the maintainer scripts
# Note: hook scripts under /etc/kernel are also executed by official Debian
# kernel packages, as well as kernel packages built using make-kpkg.
# make-kpkg sets $INITRD to indicate whether an initramfs is wanted, and
# so do we; recent versions of dracut and initramfs-tools will obey this.
debhookdir=${KDEB_HOOKDIR:-/etc/kernel}
for script in postinst postrm preinst prerm ; do
	mkdir -p "$tmpdir$debhookdir/$script.d"
	cat <<EOF > "$tmpdir/DEBIAN/$script"
#!/bin/bash

set -e

# Pass maintainer script parameters to hook scripts
export DEB_MAINT_PARAMS="\$*"

# Tell initramfs builder whether it's wanted
export INITRD=$(if_enabled_echo CONFIG_BLK_DEV_INITRD Yes No)

test -d $debhookdir/$script.d && run-parts --arg="$version" --arg="/$installed_image_path" $debhookdir/$script.d
exit 0
EOF
	chmod 755 "$tmpdir/DEBIAN/$script"
done

##
## Create sym link to kernel image
##
sed -e "s/set -e//g" -i $tmpdir/DEBIAN/postinst
sed -e "s/exit 0//g" -i $tmpdir/DEBIAN/postinst
cat >> $tmpdir/DEBIAN/postinst <<EOT 
ln -sf $(basename $installed_image_path) /boot/zImage > /dev/null 2>&1 || mv /$installed_image_path /boot/zImage
exit 0
EOT

# Try to determine maintainer and email values
if [ -n "$DEBEMAIL" ]; then
       email=$DEBEMAIL
elif [ -n "$EMAIL" ]; then
       email=$EMAIL
else
       email=$(id -nu)@$(hostname -f 2>/dev/null || hostname)
fi
if [ -n "$DEBFULLNAME" ]; then
       name=$DEBFULLNAME
elif [ -n "$NAME" ]; then
       name=$NAME
else
       name="Anonymous"
fi
maintainer="$name <$email>"

# Try to determine distribution
if [ -n "$KDEB_CHANGELOG_DIST" ]; then
        distribution=$KDEB_CHANGELOG_DIST
# In some cases lsb_release returns the codename as n/a, which breaks dpkg-parsechangelog
elif distribution=$(lsb_release -cs 2>/dev/null) && [ -n "$distribution" ] && [ "$distribution" != "n/a" ]; then
        : # nothing to do in this case
else
        distribution="unstable"
        echo >&2 "Using default distribution of 'unstable' in the changelog"
        echo >&2 "Install lsb-release or set \$KDEB_CHANGELOG_DIST explicitly"
fi

# Generate a simple changelog template
cat <<EOF > debian/changelog
$sourcename ($packageversion) $distribution; urgency=low

  * Custom built Linux kernel.

 -- $maintainer  $(date -R)
EOF

# Generate copyright file
cat <<EOF > debian/copyright
This is a packacked upstream version of the Linux kernel.

The sources may be found at most Linux archive sites, including:
https://www.kernel.org/pub/linux/kernel

Copyright: 1991 - 2017 Linus Torvalds and others.

The git repository for mainline kernel development is at:
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git

    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; version 2 dated June, 1991.

On Debian GNU/Linux systems, the complete text of the GNU General Public
License version 2 can be found in \`/usr/share/common-licenses/GPL-2'.
EOF


build_depends="bc, kmod, cpio "

# Generate a control file
cat <<EOF > debian/control
Source: $sourcename
Section: kernel
Priority: optional
Maintainer: $maintainer
Build-Depends: $build_depends
Homepage: http://www.kernel.org/
EOF

if [ "$ARCH" = "um" ]; then
	cat <<EOF >> debian/control

Package: $packagename
Architecture: any
Description: User Mode Linux kernel, version $version
 User-mode Linux is a port of the Linux kernel to its own system call
 interface.  It provides a kind of virtual machine, which runs Linux
 as a user process under another Linux kernel.  This is useful for
 kernel development, sandboxes, jails, experimentation, and
 many other things.
 .
 This package contains the Linux kernel, modules and corresponding other
 files, version: $version.
EOF

else
	cat <<EOF >> debian/control

Package: $packagename
Suggests: $fwpackagename
Architecture: any
Description: Linux kernel, version $version
 This package contains the Linux kernel, modules and corresponding other
 files, version: $version.
EOF

fi

##
## Create sym link to kernel image
##
sed -e "s/set -e//g" -i $tmpdir/DEBIAN/postinst
sed -e "s/exit 0//g" -i $tmpdir/DEBIAN/postinst
cat >> $tmpdir/DEBIAN/postinst <<EOT
if [ "\$(grep nand /proc/partitions)" != "" ] && [ "\$(grep mmc /proc/partitions)" = "" ]; then
	mkimage -A arm -O linux -T kernel -C none -a "0x40008000" -e "0x40008000" -n "Linux kernel" -d /$installed_image_path /boot/uImage  > /dev/null 2>&1
	cp /boot/uImage /tmp/uImage
	sync
	mountpoint -q /boot || mount /boot
	cp /tmp/uImage /boot/uImage
	rm -f /$installed_image_path
else
	ln -sf $(basename $installed_image_path) /boot/$image_name || mv /$installed_image_path /boot/$image_name
fi
touch /boot/.next
exit 0
EOT

##
## FAT install workaround
##
sed -e "s/set -e//g" -i $tmpdir/DEBIAN/preinst
sed -e "s/exit 0//g" -i $tmpdir/DEBIAN/preinst
cat >> $tmpdir/DEBIAN/preinst <<EOT
# exit if we are running chroot
if [ "\$(stat -c %d:%i /)" != "\$(stat -c %d:%i /proc/1/root/.)" ]; then exit 0; fi

check_and_unmount (){
	boot_device=\$(mountpoint -d /boot)

	for file in /dev/* ; do
		CURRENT_DEVICE=\$(printf "%d:%d" \$(stat --printf="0x%t 0x%T" \$file))
		if [[ "\$CURRENT_DEVICE" = "\$boot_device" ]]; then
			boot_partition=\$file
			break
		fi
	done

	bootfstype=\$(blkid -s TYPE -o value \$boot_partition)
	if [ "\$bootfstype" = "vfat" ]; then
		umount /boot
		rm -f /boot/System.map* /boot/config* /boot/vmlinuz* /boot/$image_name /boot/uImage
	fi
}
mountpoint -q /boot && check_and_unmount
EOT
echo "exit 0" >> $tmpdir/DEBIAN/preinst

# Build kernel header package
(cd $srctree; find . -name Makefile\* -o -name Kconfig\* -o -name \*.pl) > "$objtree/debian/hdrsrcfiles"
(cd $srctree; find arch/*/include include scripts -type f -o -type l) >> "$objtree/debian/hdrsrcfiles"
(cd $srctree; find security/*/include -type f) >> "$objtree/debian/hdrsrcfiles"
(cd $srctree; find arch/$SRCARCH -name module.lds -o -name Kbuild.platforms -o -name Platform) >> "$objtree/debian/hdrsrcfiles"
(cd $srctree; find $(find arch/$SRCARCH -name include -o -name scripts -type d) -type f) >> "$objtree/debian/hdrsrcfiles"
if is_enabled CONFIG_STACK_VALIDATION; then
	(cd $objtree; find tools/objtool -type f -executable) >> "$objtree/debian/hdrobjfiles"
fi
(cd $objtree; find arch/$SRCARCH/include Module.symvers include scripts -type f) >> "$objtree/debian/hdrobjfiles"
if is_enabled CONFIG_GCC_PLUGINS; then
	(cd $objtree; find scripts/gcc-plugins -name \*.so -o -name gcc-common.h) >> "$objtree/debian/hdrobjfiles"
fi
destdir=$kernel_headers_dir/usr/src/linux-headers-$version
mkdir -p "$destdir"
mkdir -p "$destdir"/net/wireguard; 
		touch "$destdir"/net/wireguard/{Kconfig,Makefile} # workaround for Wireguard
(cd $srctree; tar -c -f - -T -) < "$objtree/debian/hdrsrcfiles" | (cd $destdir; tar -xf -)
(cd $objtree; tar -c -f - -T -) < "$objtree/debian/hdrobjfiles" | (cd $destdir; tar -xf -)
(cd $objtree; cp $KCONFIG_CONFIG $destdir/.config) # copy .config manually to be where it's expected to be
ln -sf "/usr/src/linux-headers-$version" "$kernel_headers_dir/lib/modules/$version/build"
rm -f "$objtree/debian/hdrsrcfiles" "$objtree/debian/hdrobjfiles"

cat <<EOF >> debian/control

Package: $kernel_headers_packagename
Architecture: any
Description: Linux kernel headers for $KERNELRELEASE on
 This package provides kernel header files for $KERNELRELEASE on
 .
 This is useful for people who need to build external modules
EOF

# Do we have firmware? Move it out of the way and build it into a package.
if [ -e "$tmpdir/lib/firmware" ]; then
	mv "$tmpdir/lib/firmware"/* "$fwdir/lib/firmware/$version/"
	rmdir "$tmpdir/lib/firmware"

	cat <<EOF >> debian/control

Package: $fwpackagename
Architecture: all
Description: Linux kernel firmware, version $version
 This package contains firmware from the Linux kernel, version $version.
EOF

	create_package "$fwpackagename" "$fwdir"
fi

cat <<EOF >> debian/control

Package: $libc_headers_packagename
Section: devel
Provides: linux-kernel-headers
Architecture: any
Description: Linux support headers for userspace development
 This package provides userspaces headers from the Linux kernel.  These headers
 are used by the installed headers for GNU glibc and other system libraries.
EOF

if [ "$ARCH" != "um" ]; then
	create_package "$kernel_headers_packagename" "$kernel_headers_dir"
	create_package "$libc_headers_packagename" "$libc_headers_dir"
fi

create_package "$packagename" "$tmpdir"

if [ -n "$BUILD_DEBUG" ] ; then
	# Build debug package
	# Different tools want the image in different locations
	# perf
	mkdir -p $dbg_dir/usr/lib/debug/lib/modules/$version/
	cp vmlinux $dbg_dir/usr/lib/debug/lib/modules/$version/
	# systemtap
	mkdir -p $dbg_dir/usr/lib/debug/boot/
	ln -s ../lib/modules/$version/vmlinux $dbg_dir/usr/lib/debug/boot/vmlinux-$version
	# kdump-tools
	ln -s lib/modules/$version/vmlinux $dbg_dir/usr/lib/debug/vmlinux-$version

	cat <<EOF >> debian/control

Package: $dbg_packagename
Section: debug
Architecture: any
Description: Linux kernel debugging symbols for $version
 This package will come in handy if you need to debug the kernel. It provides
 all the necessary debug symbols for the kernel and its modules.
EOF

	create_package "$dbg_packagename" "$dbg_dir"
fi

if [ "x$1" = "xdeb-pkg" ]
then
    cat <<EOF > debian/rules
#!/usr/bin/make -f

build:
	\$(MAKE)

binary-arch:
	\$(MAKE) KDEB_SOURCENAME=${sourcename} KDEB_PKGVERSION=${packageversion} bindeb-pkg

clean:
	rm -rf debian/*tmp debian/files
	mv debian/ debian.backup # debian/ might be cleaned away
	\$(MAKE) clean
	mv debian.backup debian

binary: binary-arch
EOF
	mv ${sourcename}.tar.gz ../${sourcename}_${version}.orig.tar.gz
	tar caf ../${sourcename}_${packageversion}.debian.tar.gz debian/{copyright,rules,changelog,control}
	dpkg-source -cdebian/control -ldebian/changelog --format="3.0 (custom)" --target-format="3.0 (quilt)" \
		-b / ../${sourcename}_${version}.orig.tar.gz  ../${sourcename}_${packageversion}.debian.tar.gz
	mv ${sourcename}_${packageversion}*dsc ..
	dpkg-genchanges > ../${sourcename}_${packageversion}_${debarch}.changes
else
	dpkg-genchanges -b > ../${sourcename}_${packageversion}_${debarch}.changes
fi

exit 0
