#!/bin/sh
#
# newapkbuild - generate a new APKBUILD
# Copyright (c) 2009 Natanael Copa <natanael.copa@gmail.com>
#
# Distributed under GPL-2.0-only
#

program_version=3.14.1
sharedir=${ABUILD_SHAREDIR:-/usr/share/abuild}

if ! [ -f "$sharedir/functions.sh" ]; then
	echo "$sharedir/functions.sh: not found" >&2
	exit 1
fi
. "$sharedir/functions.sh"


is_url() {
	case "$1" in
	http://*|https://*|ftp://*) return 0;;
	esac
	return 1
}

is_github_url() {
	case $1 in
	https://github.com/*/*/archive/*.tar.gz) return 0;;
	esac
	return 1
}

prepare_rust() {
	cat >>APKBUILD<<__EOF__
	cargo fetch --target="\$CTARGET" --locked
__EOF__
}

# Build sections
build_make() {
	cat >>APKBUILD<<__EOF__
	make
__EOF__
}

build_autotools() {
	cat >>APKBUILD<<__EOF__
	./configure \\
		--build=\$CBUILD \\
		--host=\$CHOST \\
		--prefix=/usr \\
		--sysconfdir=/etc \\
		--mandir=/usr/share/man \\
		--localstatedir=/var
	make
__EOF__
}

build_cmake() {
# References:
# http://www.cmake.org/Wiki/CMake_Useful_Variables
# http://www.vtk.org/Wiki/CMake_Cross_Compiling
# This is incomplete: CMAKE_{HOST_,}SYSTEM_PROCESSOR needs to be set,
# and likewise CMAKE_FIND_ROOT_PATH and a few other details.

	cat >>APKBUILD<<__EOF__
	if [ "\$CBUILD" != "\$CHOST" ]; then
		local crossopts="-DCMAKE_SYSTEM_NAME=Linux -DCMAKE_HOST_SYSTEM_NAME=Linux"
	fi
	cmake -B build -G Ninja \\
		-DCMAKE_INSTALL_PREFIX=/usr \\
		-DCMAKE_INSTALL_LIBDIR=lib \\
		-DBUILD_SHARED_LIBS=ON \\
		-DCMAKE_BUILD_TYPE=None \\
		\$crossopts
	cmake --build build
__EOF__
}

build_meson() {
# References:
# http://mesonbuild.com/Reference-manual.html
# http://mesonbuild.com/Cross-compilation.html

	cat >>APKBUILD<<__EOF__
	abuild-meson \\
		output .
	meson compile -C output
__EOF__
}

build_perl() {
	cat >>APKBUILD<<__EOF__
	perl Makefile.PL INSTALLDIRS=vendor
	make
__EOF__
}

build_python() {
	cat >>APKBUILD<<__EOF__
	python3 setup.py build
__EOF__
}

build_python_gpep517() {
	cat >>APKBUILD<<__EOF__
	gpep517 build-wheel \\
		--wheel-dir .dist \\
		--output-fd 3 3>&1 >&2
__EOF__
}

build_rust() {
	cat >>APKBUILD<<__EOF__
	cargo auditable build --frozen --release
__EOF__
}

build_empty() {
	cat >>APKBUILD<<__EOF__
	# Replace with proper build command(s).
	# Remove if there is no build command.
	:
__EOF__
}

# Check sections
check_make() {
	cat >>APKBUILD<<__EOF__
	make check
__EOF__
}

check_cmake() {
	cat >>APKBUILD<<__EOF__
	ctest --test-dir build
__EOF__
}

check_python() {
	cat >>APKBUILD<<__EOF__
	pytest
__EOF__
}

check_python_gpep517() {
	cat >>APKBUILD<<__EOF__
	python3 -m venv --clear --without-pip --system-site-packages .testenv
	.testenv/bin/python3 -m installer .dist/*.whl
	.testenv/bin/python3 -m pytest
__EOF__
}

check_empty() {
	cat >>APKBUILD<<__EOF__
	# Replace with proper check command(s).
	# Remove and add !check option if there is no check command.
	:
__EOF__
}

check_meson() {
	cat >>APKBUILD<<__EOF__
	meson test --no-rebuild --print-errorlogs -C output
__EOF__
}

check_rust() {
	cat >>APKBUILD<<__EOF__
	cargo test --frozen
__EOF__
}

# Package sections
package_make() {
	cat >>APKBUILD<<__EOF__
	make DESTDIR="\$pkgdir" install
__EOF__
}

package_cmake() {
	cat >>APKBUILD<<__EOF__
	DESTDIR="\$pkgdir" cmake --install build
__EOF__
}

package_autotools() {
	package_make
}

package_meson() {
	cat >>APKBUILD<<__EOF__
	DESTDIR="\$pkgdir" meson install --no-rebuild -C output
__EOF__
}

package_perl() {
	cat >>APKBUILD<<__EOF__
	make DESTDIR="\$pkgdir" install
	find "\$pkgdir" \\( -name perllocal.pod -o -name .packlist \\) -delete
__EOF__
}

package_python() {
	cat >>APKBUILD<<__EOF__
	python3 setup.py install --prefix=/usr --root="\$pkgdir"
__EOF__
}

package_python_gpep517() {
	cat >>APKBUILD<<__EOF__
	python3 -m installer -d "\$pkgdir" \\
		.dist/*.whl
__EOF__
}

package_empty() {
	cat >>APKBUILD<<__EOF__
	# Replace with proper package command(s)
	:
__EOF__
}

package_rust() {
	cat >>APKBUILD<<__EOF__
	# Replace with proper package command(s)
	:
__EOF__
}

# Create new aport from templates
newaport() {
	local newname="${1##*/}"
	local pn=${newname%[-_][0-9]*}
	local prearchive postarchive pv
	local source=
	is_url "$1" && source="$1"

	if is_github_url $source; then
		if [ -z "$pkgname" ]; then
			pkgname=${source%/archive/*}
			pkgname=${pkgname##*/}
		fi
		pv=${newname%.t*} #strip .tar.gz .tgz .tar.bz2 etc
		pv=${pv#*[a-z]}
		prearchive=${source%/archive/*}
		postarchive=${source#*/archive/}
		postarchive=${postarchive#refs/tags/}
		source="${prearchive}/archive/${postarchive}"
		source="${source%.t*}/$pkgname-$pv.tar.gz"
	elif [ "$pn" != "$newname" ]; then
		pv=${newname#$pn[-_]}
		pv=${pv%.t*} #strip .tar.gz .tgz .tar.bz2 etc
	fi
	if [ -z "$pkgname" ]; then
		pkgname=$pn
	fi
	if [ -e "$pkgname"/APKBUILD ] && [ -z "$force" ]; then
		error "$pkgname/APKBUILD already exists"
		return 1
	fi
	mkdir -p "$pkgname"
	cd "$pkgname"

	if [ -z "$source" ] && [ -n "$sourceforge" ]; then
		source="https://downloads.sourceforge.net/$pn/$pn-$pv.tar.gz"
	fi

	if [ -z "$depends" ] && [ "$buildtype" = "perl" ]; then
		depends="perl"
	fi

	if [ -z "$checkdepends" ] && [ "$buildtype" = "python"* ]; then
		checkdepends="py3-pytest"
	fi

	case "$buildtype" in
	python) makedepends="py3-setuptools";;
	python_gpep517) makedepends="py3-gpep517 py3-setuptools py3-wheel";;
	cmake)  makedepends="cmake samurai";;
	meson)  makedepends="meson";;
	rust)   makedepends="cargo cargo-auditable";;
	*)      makedepends="\$depends_dev";;
	esac

	# Replace pkgver in $source
	if [ -n "$source" ]; then
		source=$(echo "$source" | sed "s/$pv/\$pkgver/g")
	fi

	# Copy init.d scripts if requested
	if [ -n "$cpinitd" ]; then
		cp "$sharedir"/sample.initd $pkgname.initd
		cp "$sharedir"/sample.confd $pkgname.confd
		cp "$sharedir"/sample.pre-install $pkgname.pre-install
		cp "$sharedir"/sample.post-install $pkgname.post-install
		install="\$pkgname.pre-install \$pkgname.post-install"
		source="$source
	$pkgname.initd
	$pkgname.confd
	"
	fi

	# Generate header with standard variables
	cat >APKBUILD<<__EOF__
# Contributor:${PACKAGER:+" "}${PACKAGER}
# Maintainer:${MAINTAINER:+" "}${MAINTAINER}
pkgname=$pkgname
pkgver=$pv
pkgrel=0
pkgdesc="$pkgdesc"
url="$url"
arch="all"
license="$license"
depends="$depends"
depends_dev=""
makedepends="$makedepends"
checkdepends="$checkdepends"
install="$install"
subpackages="\$pkgname-dev \$pkgname-doc"
source="$source"
__EOF__

	abuild -f fetch checksum unpack
	# Figure out the builddir
	for i in src/*; do
		if [ -d "$i" ]; then
			sdir=$i
			builddir=$(echo ${i#*/} | sed "s/$pv/\$pkgver/g")
		fi
	done
	printf 'builddir="$srcdir/%s"\n\n' "$builddir" >> APKBUILD

	# Subpackage -dev is usually required only for C/C++. Since depends_dev
	# confuses a lot people, remove it if there's no .h or .hpp file.
	find "$sdir" -name "*.h" -o -name "*.hpp" -maxdepth 3 2>/dev/null \
		| head -n 1 | grep -q ".*" \
		|| sed -i -e '/^depends_dev=.*/d' -e 's/\$depends_dev\s*//' APKBUILD

	# Try to autodetect the buildtype
	if [ -z "$buildtype" ]; then
		if [ -x "$sdir"/configure ]; then
			buildtype="autotools"
		elif [ -r "$sdir"/Makefile.PL ] || [ "${pn#perl-}" != "$pn" ]; then
			buildtype="perl"
		elif [ -r "$sdir"/waf ]; then
			buildtype="waf"
		elif [ -r "$sdir"/meson.build ]; then
			buildtype="meson"
		elif [ -d "$sdir"/cmake ] || [ -r "$sdir/CMakeLists.txt" ]; then
			buildtype="cmake"
		elif [ -r "$sdir"/Makefile ]; then
			buildtype="make"
		elif [ -r "$sdir"/setup.py ] || [ "${pn#py[0-9]-}" != "$pn" ]; then
			buildtype="python"
		elif [ -r "$sdir"/pyproject.toml ] || [ "${pn#py[0-9]-}" != "$pn" ]; then
			buildtype="python_gpep517"
		elif [ -r "$sdir"/Cargo.toml ]; then
			buildtype="rust"
		fi
	fi

	case "$buildtype" in
	rust)
		cat >>APKBUILD<<__EOF__
prepare() {
	default_prepare

__EOF__
		prepare_rust
		cat >>APKBUILD<<__EOF__
}

__EOF__
		;;
	esac

	# Create build() function
	cat >>APKBUILD<<__EOF__
build() {
__EOF__

	case "$buildtype" in
	make)
		build_make;;
	cmake)
		build_cmake;;
	meson)
		build_meson;;
	autotools)
		build_autotools;;
	perl)
		build_perl;;
	python)
		build_python;;
	python_gpep517)
		build_python_gpep517;;
	rust)
		build_rust;;
	*)
		build_empty;;
	esac

	cat >>APKBUILD<<__EOF__
}

__EOF__

	# Create check() function
	cat >>APKBUILD<<__EOF__
check() {
__EOF__

	case "$buildtype" in
	make|autotools|perl)
		check_make;;
	cmake)
		check_cmake;;
	meson)
		check_meson;;
	python)
		check_python;;
	python_gpep517)
		check_python_gpep517;;
	rust)
		check_rust;;
	*)
		check_empty;;
	esac

	cat >>APKBUILD<<__EOF__
}

__EOF__

	# Create package() function
	cat >>APKBUILD<<__EOF__
package() {
__EOF__

	case "$buildtype" in
	make)
		package_make;;
	cmake)
		package_cmake;;
	autotools)
		package_autotools;;
	meson)
		package_meson;;
	perl)
		package_perl;;
	python)
		package_python;;
	python_gpep517)
		package_python_gpep517;;
	rust)
		package_rust;;
	*)
		package_empty;;
	esac

	if [ -n "$cpinitd" ]; then
		cat >>APKBUILD<<__EOF__

	install -m755 -D "\$srcdir"/\$pkgname.initd \\
		"\$pkgdir"/etc/init.d/\$pkgname
	install -m644 -D "\$srcdir"/\$pkgname.confd \\
		"\$pkgdir"/etc/conf.d/\$pkgname
__EOF__
	fi
	cat >>APKBUILD<<__EOF__
}

__EOF__

	# regenerate checksum so they end last in APKBUILD
	abuild -f checksum
}

usage() {
	cat >&2 <<-__EOF__
		$program $program_version - generate a new APKBUILD
		Usage: $program [-n PKGNAME] [-d PKGDESC] [-l LICENSE] [-u URL]
		       [-a | -C | -m | -p | -y | -r] [-s] [-c] [-f] [-h]
		       PKGNAME[-PKGVER] | SRCURL
		Options:
		  -n  Set package name to PKGNAME (only use with SRCURL)
		  -d  Set package description to PKGDESC
		  -l  Set package license to LICENSE, use identifiers from:
		      <https://spdx.org/licenses/>
		  -u  Set package URL
		  -a  Create autotools package (use ./configure ...)
		  -C  Create CMake package (Assume cmake/ is there)
		  -m  Create meson package (Assume meson.build is there)
		  -p  Create perl package (Assume Makefile.PL is there)
		  -y  Create python setuptools package (Assume setup.py is there)
		  -e  Create python gpep517 package (Assume pyproject.toml is there)
		  -r  Create rust package (Assume Cargo.toml is there)
		  -s  Use sourceforge source URL
		  -c  Copy a sample init.d, conf.d, and install script
		  -f  Force even if directory already exists
		  -h  Show this help
	__EOF__
}

set_buildtype() {
	if [ -n "$buildtype" ]; then
		error "More than one buildtype flag specified ($buildtype and $1)"
		exit 1
	fi
	buildtype="$1"
}

check_arguments() {
	if [ $# -eq 0 ]; then
		error "Missing required argument: PKGNAME[-PKGVER] | SRCURL"
		exit 1
	fi
	if [ $# -gt 1 ]; then
		shift
		error "Unrecognized arguments: $*"
		exit 1
	fi
	if [ -n "$1" ] && ! is_url "$1" && [ -n "$pkgname" ]; then
		error "-n is only allowed when using SRCURL as last argument"
		exit 1
	fi
	if is_url "$1" && [ -n "$sourceforge" ]; then
		error "-s is only allowed when using PKGNAME as last argument"
		exit 1
	fi
}

while getopts "acCmd:fhl:n:pyeu:sr" opt; do
	case $opt in
		'a') set_buildtype "autotools";;
		'c') cpinitd=1;;
		'C') set_buildtype "cmake";;
		'm') set_buildtype "meson";;
		'd') pkgdesc="$OPTARG";;
		'f') force=1;;
		'h') usage; exit;;
		'l') license="$OPTARG";;
		'n') pkgname="$OPTARG";;
		'p') set_buildtype "perl";;
		'y') set_buildtype "python";;
		'e') set_buildtype "python_gpep517";;
		'r') set_buildtype "rust";;
		'u') url="$OPTARG";;
		's') sourceforge=1;;
	esac
done
shift $(( $OPTIND - 1 ))

check_arguments "$@"
newaport $1
