#!/bin/sh -ef
#
# The source package rebuild functions for the hsh-rebuild.
#
# Copyright (C) 2003-2022  Dmitry V. Levin <ldv@altlinux.org>
# All rights reserved.
#
# SPDX-License-Identifier: GPL-2.0-or-later
#

source=
sname=

build_deps=
calc_check_install_build_deps()
{
	build_deps="$(wlimit_time_elapsed=$wlimit_time_short \
		      wlimit_time_idle=$wlimit_time_short \
		      wlimit_bytes_written=$wlimit_bytes_out \
		      chrootuid2 </dev/null)" &&
		verbose "$sname: fetched build dependencies." ||
		fatal "$sname: failed to fetch build dependencies."
	build_deps="$(printf %s "$build_deps" |LC_ALL=C grep -v '^rpmlib(' |LC_ALL=C tr -d [[:blank:]])"
	verbose "$sname: calculated build dependencies: $(printf %s "$build_deps" |tr -s '[:space:]' ' ')"

	local bad_deps
	if bad_deps="$(printf %s "$build_deps" |LC_ALL=C grep -v '^[[:alnum:]_/]')"; then
		fatal "$sname: invalid build dependencies: $(printf %s "$bad_deps" |tr -s '[:space:]' ' ')"
	fi

	if [ -n "$build_deps" ]; then
		hsh-install --no-lock \
			${quiet:+$quiet} \
			${verbose:+$verbose} \
			${number:+--number=$number} \
			${exclude_docs:+--excludedocs} \
			${hasher_priv_dir:+--hasher-priv-dir="$hasher_priv_dir"} \
			${known_mountpoints:+--mountpoints="$known_mountpoints"} \
			-- \
			"$workdir" $build_deps
	fi
} # calc_check_install_build_deps

create_filter_spec_buildreq()
{
	cat > "$chroot"/.host/filter_spec_buildreq <<\__EOF__
#!/bin/sh -ef
tr -s '[:blank:],' ' ' |
	while read REPLY; do
		set -- $REPLY
		while [ -n "$1" ]; do
			case "$2" in
				=|["<>"]|["<>"]=)
					[ -z "${1##*%*}" -o -z "${3##*%*}" ] ||
						printf %s\\n "$1 $2 $3"
					shift 2
					;;
				*)
					[ -z "${1##*%*}" ] ||
						printf %s\\n "$1"
					;;
			esac
			if ! shift; then
				echo >&2 "Invalid build dependencies: $REPLY"
				exit 1
			fi
		done
	done
__EOF__
	chmod 755 -- "$chroot"/.host/filter_spec_buildreq
}

make_srpm_from_pkgtar()
{
	# srcrpmdir: where to put the resulting srpm (on the "foreign" side).
	# The value must be quoted appropriately, so that it can be inserted
	# into shell code text (example: srcrpmdir='"$HOME/in/srpm"'):
	local srcrpmdir="${1-}" # optional; absent/empty means default
	local srpm_fast_payload=
	[ -n "$build_srpm_only" ] ||
		srpm_fast_payload="--define '_source_payload w1.gzdio'"
	if [ -z "$nodeps" ]; then
		# Calculate, check and install BuildRequires(pre) dependencies.
		create_filter_spec_buildreq
		create_entry_header
		cat >>"$entry" <<__EOF__
sed '/^buildrequires(pre):[[:space:]]*/I!d;s///' "\$HOME/in/spec"/* |
	/.host/filter_spec_buildreq
__EOF__
		calc_check_install_build_deps

		local buildreq_helper
		buildreq_helper="$(type -p hsh-buildreq-filter)" &&
			[ -r "$buildreq_helper" ] ||
			fatal 'hsh-buildreq-filter: Not available'

		install -p -m755 $verbose "$buildreq_helper" chroot/.host/hsh-buildreq-filter >&2

		# Calculate, check and install other build dependencies.
		create_entry_header
		cat >>"$entry" <<__EOF__
spec="\$(rpmbuild -bE \\
	--target="$(quote_shell "${target:-$def_target}")" \\
	$rpmargs \\
	--define "_specdir \$HOME/in/spec" \\
	--define "_sourcedir \$HOME/in/source" \\
	-- "\$HOME/in/spec"/*)" || exit
printf %s "\$spec" |
	/.host/hsh-buildreq-filter |
	/.host/filter_spec_buildreq
__EOF__
		calc_check_install_build_deps
	fi

	# At this stage, build environment should be ready for creating source rpm.
	create_entry_header
	cat >>"$entry" <<__EOF__
${srcrpmdir:+mkdir $verbose -m700 -- $srcrpmdir >&2}
SOURCE_DATE_EPOCH="\$(cat -- "\$HOME/in/SOURCE_DATE_EPOCH")"
export SOURCE_DATE_EPOCH
rpmbuild -bs --nodeps \\
	--target="$(quote_shell "${target:-$def_target}")" \\
	--define '_allow_undefined_macros 1' \\
	$rpmargs \\
	$srpm_fast_payload \\
	--define "_specdir \$HOME/in/spec" \\
	--define "_sourcedir \$HOME/in/source" \\
	${srcrpmdir:+--define '_srcrpmdir '$srcrpmdir} \\
	-- "\$HOME/in/spec"/*
# Once srpm is created, its sources are no longer needed.
rm -rf "\$HOME/in/spec" "\$HOME/in/source"
__EOF__
	wlimit_time_elapsed=$wlimit_time_long wlimit_time_idle=$wlimit_time_long wlimit_bytes_written=$wlimit_bytes_out \
	    chrootuid2 </dev/null &&
		verbose "$sname: created src.rpm file." ||
		fatal "$sname: failed to create src.rpm file"
} # make_srpm_from_pkgtar

source_package_type=
install_source_package()
{
	copy_chroot_incoming "$source"
	create_entry_header
	cat >>"$entry" <<__EOF__
mkdir -p $verbose -m700 -- "\$TMPDIR" >&2
rm -rf "\$HOME/in"
mkdir $verbose -m700 -- "\$HOME/in" >&2
# Check source file.
header="\$(od -A n -N 8 -t x1 -- "$(quote_shell "$sname")")"
case "\$header" in
	' ed ab ee db '??' '??' 00 01')
		mkdir $verbose -m700 -- "\$HOME/in/srpm" >&2
		install -pm644 $verbose "$(quote_shell "$sname")" "\$HOME/in/srpm/" >&2
		rpmquery --qf '%{buildtime}\\n' -p -- "$(quote_shell "$sname")" > "\$HOME/in/SOURCE_DATE_EPOCH"
		echo src.rpm
		exit 0
		;;
	' ed ab ee db '*)
		exit 0
		;;
esac
spec="\$(LC_ALL=C tar -tvf "$(quote_shell "$sname")" 2>/dev/null |
	sed '/^V---------[[:space:]]\+0\/0[[:space:]]\+0[[:space:]]\+[^[:space:]]\+[[:space:]]\+[^[:space:]]\+[[:space:]]\+/!d;s///;s/--Volume Header--\$//;q')"

case "\$spec" in
	*/*)
		type="\${spec%/*}"
		spec="\${spec##*/}"
		case "\$type" in
			img|oci) ;;
			*) exit 0 ;;
		esac
		;;
	*) type=pkg.tar ;;
esac

if [ -n "\$spec" ]; then
	mkdir $verbose -m700 -- "\$HOME/in/spec" "\$HOME/in/source" >&2
	tar $verbose -xf "$(quote_shell "$sname")" -C "\$HOME/in/source/" >&2
	mv $verbose "\$HOME/in/source/\$spec" "\$HOME/in/spec/" >&2
	date +%s -r "\$HOME/in/spec"/\$spec > "\$HOME/in/SOURCE_DATE_EPOCH"
	echo "\$type"
	exit 0
fi
__EOF__
	source_package_type="$(wlimit_time_elapsed=$wlimit_time_short wlimit_time_idle=$wlimit_time_short wlimit_bytes_written=$wlimit_bytes_out \
		       chrootuid2 </dev/null)" &&
		verbose "$sname: installed source file." ||
		fatal "$sname: failed to install source file."
	purge_chroot_in
	case "$source_package_type" in
		src.rpm)
			;;
		pkg.tar)
			make_srpm_from_pkgtar '"$HOME/in/srpm"'
			;;
		img|oci)
			;;
		*)
			fatal "$sname does not look like source package."
			;;
	esac
} # install_source_package

run_sisyphus_check()
{
	local dir="$1" no_check="$2" add_gpg="${3-}"

	[ -n "$no_check" ] ||
		no_check="$no_sisyphus_check"
	[ "$no_check" != all ] || return 0

	if [ -n "$no_check" ]; then
		[ -z "$add_gpg" ] ||
			no_check="$no_check,gpg"
	else
		no_check=gpg
	fi

	create_entry_header
	cat >>"$entry" <<__EOF__
sisyphus_check --no-check="$(quote_shell "$no_check")" "$dir"
__EOF__

	wlimit_time_elapsed=$wlimit_time_long wlimit_time_idle=$wlimit_time_long wlimit_bytes_written=$wlimit_bytes_out \
	    chrootuid2 </dev/null &&
		verbose "$sname: sisyphus_check passed." ||
		fatal "$sname: sisyphus_check failed."
} # run_sisyphus_check

install_deps_pkg()
{
	# Create query script.
	if [ -n "$prog_query_req" ]; then
		install -p -m755 $verbose "$prog_query_req" chroot/.host/query_req >&2
	elif [ -n "$query_repackage" ] && [ "$source_package_type" = src.rpm ]; then
		# Unpack srpm and treat it as pkg.tar to calculate the build deps.
		# Since rpm-4.13, we could use rpmspec for a lightweight query instead
		# of creating an srpm; for now, we exclude the sources and patches.
		create_entry_header
		cat >>"$entry" <<__EOF__
cd "\$HOME/in/srpm"
${rpmi:-$def_rpmi} -i \\
	--define "_specdir \$HOME/in/spec" \\
	--define "_sourcedir \$HOME/in/source" \\
-- *
sed -i -r\\
	-e 's/^[[:blank:]]*(Source|Patch)([0-9]+)[[:blank:]]*:.*\$/&\nNo\1: \2/I' \\
	-e 's/^[[:blank:]]*(Source|Patch)[[:blank:]]*:.*\$/&\nNo\1: 0/I' \\
-- \$HOME/in/spec/*
__EOF__
		chrootuid2 </dev/null &&
			verbose "$sname: unpacked srpm." ||
			fatal "$sname: failed to unpack srpm."

		make_srpm_from_pkgtar '"$HOME/in/nosrpm"'

		# The repackaged in/nosrpm/*.(no)src.rpm is
		# only used to query the build dependencies;
		# the original in/srpm/*.src.rpm is used for the final rebuild.
		# It may seem that if the build dependencies have shrunk
		# after repackaging, then the final build could fail because
		# the original srpm would list more build dependencies.
		# This is not a problem, however, because rpmbuild --rebuild
		# calculates correctly the new set of build deps;
		# --nodeps is not needed for it to work properly.

		cat >chroot/.host/query_req <<__EOF__
#!/bin/sh -e
cd "\$HOME/in/nosrpm"
rpmquery -pR -- *.*src.rpm
__EOF__
	else
		cat >chroot/.host/query_req <<__EOF__
#!/bin/sh -e
rpmquery -pR -- "\$@"
__EOF__
	fi
	chmod 755 chroot/.host/query_req

	create_entry_header
	cat >>"$entry" <<__EOF__
cd "\$HOME/in/srpm"
/.host/query_req *
__EOF__

	calc_check_install_build_deps
	calculate_required_mountpoints "$build_deps"
	calculate_required_mountpoints_from_installed
}

install_img_build()
{
	hsh-install --no-lock \
		${quiet:+$quiet} \
		${verbose:+$verbose} \
		${number:+--number=$number} \
		${exclude_docs:+--excludedocs} \
		${hasher_priv_dir:+--hasher-priv-dir="$hasher_priv_dir"} \
		${known_mountpoints:+--mountpoints="$known_mountpoints"} \
		-- \
		"$workdir" img-build
}

install_deps_img()
{
	local prog_query_req="${1-}"

	install_img_build
	# Create query script.
	if [ -n "$prog_query_req" ]; then
		install -p -m755 $verbose "$prog_query_req" chroot/.host/query_req >&2
	else
		cat >chroot/.host/query_req <<__EOF__
#!/bin/sh -e
img-build \\
	--target="$(quote_shell "${target:-$def_target}")" \\
	--requires \\
	-- "\$@"
__EOF__
	fi
	chmod 755 chroot/.host/query_req

	create_entry_header
	cat >>"$entry" <<__EOF__
cd "\$HOME/in/spec"
/.host/query_req *
__EOF__

	calc_check_install_build_deps
	calculate_required_mountpoints "$build_deps"
	calculate_required_mountpoints_from_installed

	for mpoint in $(printf %s "$repo_mountpoints" |tr -s , ' '); do
		[ ! -d chroot/"$mpoint" ] || continue
		create_entry_fakeroot_header
		cat >>"$entry" <<__EOF__
cd /
mkdir -p -- "$mpoint"
__EOF__
		wlimit_time_elapsed=$wlimit_time_short wlimit_time_idle=$wlimit_time_short \
		    chrootuid1 &&
			verbose "Created $mpoint mount point." ||
			fatal "Failed to create $mpoint mount point."
	done

	[ -z "$required_mountpoints" ] &&
		required_mountpoints="$repo_mountpoints" ||
		required_mountpoints="$required_mountpoints,$repo_mountpoints"
}

run_rebuild()
{
	create_entry_header
	cat >>"$entry" <<__EOF__
cd "\$HOME/in/srpm"
packager=\$(rpmquery -p --qf '%{PACKAGER}' *)
cat >>"\$HOME/.rpmmacros" <<EOF
%packager \$packager
EOF

target="$(quote_shell "${target:-$def_target}")" /.host/rebuild *
find "\$HOME/RPM/SRPMS/" -mindepth 1 -maxdepth 1 -type f -name \*.src.rpm -print0 |
	xargs -r0 mv -f $verbose --target-directory=/.out/ -- >&2
find "\$HOME/RPM/RPMS/" -mindepth 2 -maxdepth 2 -type f -name \*.rpm -print0 |
	xargs -r0 mv -f $verbose --target-directory=/.out/ -- >&2
__EOF__

	requested_mountpoints="$required_mountpoints" \
	    chrootuid2 </dev/null &&
		verbose "rebuild of \`$sname' complete." ||
		fatal "rebuild of \`$sname' failed."
} # run_rebuild

run_rebuild_img()
{
	create_entry_header
	cat >>"$entry" <<__EOF__
cd "\$HOME/in/spec"

target="$(quote_shell "${target:-$def_target}")" /.host/rebuild *
find "\$HOME/out/" -mindepth 1 -maxdepth 1 -type f -print0 |
	xargs -r0 mv -f $verbose --target-directory=/.out/ -- >&2
__EOF__

	requested_mountpoints="$required_mountpoints" \
	    chrootuid2 </dev/null &&
		verbose "rebuild of \`$sname' complete." ||
		fatal "rebuild of \`$sname' failed."
} # run_rebuild_img

build_srpm_only=
nodeps=
build_pkg()
{
	# Create rebuild script.
	if [ -z "$build_srpm_only" ]; then
		if [ -n "$prog_rebuild" ]; then
			install -p -m755 $verbose "$prog_rebuild" chroot/.host/rebuild >&2
		else
			cat >"chroot/.host/rebuild" <<__EOF__
#!/bin/sh -le
${verbose:+set -x}
export -n target ||:
SOURCE_DATE_EPOCH="\$(cat -- "\$HOME/in/SOURCE_DATE_EPOCH")"
export SOURCE_DATE_EPOCH
exec >&2 time rpmbuild --rebuild --target="\$target" $nodeps $rpmargs "\$@"
__EOF__
			chmod 755 "chroot/.host/rebuild"
			verbose 'Created rebuild script.'
		fi

		# Build the package.
		purge_chroot_out
		run_rebuild

		# Execute sisyphus_check for output.
		run_sisyphus_check '/.out' "$no_sisyphus_check_out" gpg
	else
		create_entry_header
		cat >>"$entry" <<__EOF__
find "\$HOME/in/srpm" -mindepth 1 -maxdepth 1 -type f -name \*.src.rpm -print0 |
	xargs -r0 mv -f $verbose --target-directory=/.out/ -- >&2
__EOF__
		chrootuid2 </dev/null &&
			verbose "copying of src.rpm for \`$sname' complete." ||
			fatal "copying of src.rpm for \`$sname' failed."
	fi
}

copy_apt_conf()
{
	local img_apt_dir="$aptbox"/etc/apt
	if [ -n "$img_apt_config" ]; then
		rm -rf "$img_workdir"
		mkdir -p -m700 $verbose "$img_workdir"
		mkaptbox --no-lock "$img_workdir" \
			${target:+--target="$target"} \
			${quiet:+$quiet} \
			${verbose:+$verbose} \
			--apt-config="$img_apt_config" \
			--no-stuff \
			--no-update \
			#
		img_apt_dir="$img_workdir"/aptbox/etc/apt
	fi

	cp -a "$img_apt_dir" chroot/.in

	create_entry_fakeroot_header
	cat >>"$entry" <<-'__EOF__'
	apt_conf_dir="$HOME"/apt
	rm -rf "$apt_conf_dir"
	cp -aT /.in/apt "$apt_conf_dir"

	sed '1d' -i "$apt_conf_dir"/sources.list

	apt_config_file="$apt_conf_dir/apt.conf"
	cat >"$apt_config_file" <<__EOF__EOF__
	Dir::Etc::main "/dev/null";
	Dir::Etc::parts "/var/empty";
	Dir::Etc::sourceparts "/var/empty";
	Dir::Etc::sourcelist "$apt_conf_dir/sources.list";
	Dir::Etc::preferences "$apt_conf_dir/preferences";
	Dir::Etc::preferencesparts "$apt_conf_dir/preferences.d";
	Dir::Etc::pkgpriorities "$apt_conf_dir/pkgpriorities";
	__EOF__EOF__
	__EOF__
	chrootuid2 </dev/null &&
		verbose "Copied apt configuration." ||
		fatal "Failed to copy apt configuration."
	purge_chroot_in

	if [ -n "$img_apt_config" ]; then
		rm -rf "$img_apt_dir" &&
			verbose 'Removed temporary aptbox for image building' ||
			fatal 'Failed to remove temporary aptbox for image building'
	fi
}

build_img()
{
	if [ -n "$prog_rebuild" ]; then
		install -p -m755 $verbose "$prog_rebuild" chroot/.host/rebuild >&2
	else
		cat >"chroot/.host/rebuild" <<__EOF__
#!/bin/sh -le
${verbose:+set -x}
export -n target ||:
SOURCE_DATE_EPOCH="\$(cat -- "\$HOME/in/SOURCE_DATE_EPOCH")"
export SOURCE_DATE_EPOCH
exec >&2 time img-build --target="\$target" --sourcedir "\$HOME"/in/source "\$@"
__EOF__
		chmod 755 "chroot/.host/rebuild"
		verbose 'Created rebuild script.'
	fi

	# Build the package.
	purge_chroot_out
	run_rebuild_img
}

# Copy binaries from chroot.
copy_results_pkg()
{
	case `find chroot/.out/ -mindepth 1 -maxdepth 1 -type f -name \*.src.rpm |LC_ALL=C grep -c '\.src\.rpm$'` in
		0|1) ;;
		*) fatal 'Too many src.rpm files written.' ;;
	esac

	make_repo
	repo_bin="${repo_bin:-${repo:-$def_repo}/"${target:-$def_target}"/RPMS.hasher/}"
	repo_src="${repo_src:-${repo:-$def_repo}/SRPMS.hasher/}"

	find chroot/.out/ -mindepth 1 -maxdepth 1 -type f -name \*.src.rpm -print0 |
		xargs -r0 mv -f $verbose --target-directory="$repo_src" -- >&2

	find chroot/.out/ -mindepth 1 -maxdepth 1 -type f -name \*.rpm -print0 |
		xargs -r0 mv -f $verbose --target-directory="$repo_bin" -- >&2
	purge_chroot_out
}

# Copy images from the chroot.
copy_results_img()
{
	images="${images:-${def_images}"/${target:-$def_target}"}"
	mkdir -p $verbose -- "$images" >&2

	find chroot/.out/ -mindepth 1 -maxdepth 1 -type f -print0 |
		xargs -r0 mv -f $verbose --target-directory="$images" -- >&2

	purge_chroot_out
}
