#!/usr/bin/env bash
# $Id: make-archive 14 2026-10-26 11:52:27Z peter $
#
# Copyright (C) 2026 Péter Felecan
# Assisted by Claude Sonnet 5.5 (Anthropic)
#
# 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 3 of the
# License, or (at your option) any later version.
#
# Creates the project's distribution archive (sapupra-VERSION.tar.xz).
#
# This is the project's only delivery mechanism (the author's explicit rule,
# see DevelopmentStandard.org, "Delivery Archive"): every shipped file
# sits at the archive's own top level, no enclosing sapupra-VERSION/
# directory, and .tar-exclude is the authoritative list of what a
# delivery omits. automake's `make dist-xz` is NOT a substitute.
#
# Run as the final step, after bootstrap/configure/make pdf html (to
# verify the docs build) and make maintainer-clean (to get back to a
# pristine tree) --- this script does not itself build or clean
# anything.

set -e

cd "$(dirname "$0")"

# An executable bit lost on a shipped script during an edit went
# unnoticed, in an earlier project with this same toolchain, until a
# delivered archive was extracted. Check each script is still
# executable before packaging; a script added to the project is added
# here in the same change.
scripts_to_check="bootstrap make-archive stale-files man2htmldoc orgdoc-check"
for script in ${scripts_to_check}
do
	if [ ! -e "${script}" ]
	then
		echo "make-archive: ${script} is missing, aborting (restore it, or remove it from scripts_to_check if it was dropped on purpose)" >&2
		exit 1
	fi
	if [ ! -x "${script}" ]
	then
		echo "make-archive: ${script} is not executable, aborting (chmod +x ${script})" >&2
		exit 1
	fi
done

version="$(sed -n '2p' VERSION)"
archive="sapupra-${version}.tar.xz"

# Write to a temp file OUTSIDE the directory being archived, then
# move it into place: writing the growing output file directly into "." (even with
# --exclude) makes the directory's own metadata change while tar is
# still mid-scan of that same directory.
tmp_archive="$(mktemp "${TMPDIR:-/tmp}/sapupra-archive-XXXXXX.tar.xz")"
rm -f "${archive}"
# The archive carries a MANIFEST, the list of the files it ships:
# stale-files compares a directory with it, to find the files that a
# release dropped and that an archive unpacked over an older tree does
# not remove. It is appended to the uncompressed tar after the main
# pass, because .tar-exclude leaves MANIFEST out (a tree that was
# unpacked from an archive has one; the new one is the right one).
tmp_tar="$(mktemp "${TMPDIR:-/tmp}/sapupra-archive-XXXXXX.tar")"
tmp_manifest_dir="$(mktemp -d "${TMPDIR:-/tmp}/sapupra-manifest-XXXXXX")"
tar cf "${tmp_tar}" --exclude-from=.tar-exclude .
tar tf "${tmp_tar}" | sed -e 's|^\./||' | grep -v '/$' | grep -v '^$' | LC_ALL=C sort > "${tmp_manifest_dir}/MANIFEST"
( cd "${tmp_manifest_dir}" && tar rf "${tmp_tar}" --transform 's|^|./|' MANIFEST )
xz -c "${tmp_tar}" > "${tmp_archive}"
rm -rf "${tmp_tar}" "${tmp_manifest_dir}"

# A stray file in the tree is archived like any other: the tree IS what
# is shipped. Refuse any top-level entry that Makefile.am does not name
# (a tool's leftover directory rode along in a packpay archive and only
# the layout check of the delivery noticed). configure.ac is automake's
# own input and is not named there.
strays=""
for name in $(tar tf "${tmp_archive}" | sed -e 's|^\./||' -e 's|/.*||' | sort -u)
do
	[ -n "${name}" ] || continue
	[ "${name}" = "configure.ac" ] && continue
	grep -qwF -- "${name}" Makefile.am || strays="${strays} ${name}"
done
if [ -n "${strays}" ]
then
	rm -f "${tmp_archive}"
	echo "make-archive: not named in Makefile.am (a stray file in the tree?):${strays}" >&2
	exit 1
fi

mv "${tmp_archive}" "${archive}"

echo "wrote ${archive}"

# Local Variables:
# mode: shell-script
# End:
