#!/bin/sh
#
# Generate generic POSIX compliant Makefiles.
#
# This means that there's a lot of unnecessary text (when using BSD or GNU
# make, as I'm sure there are in other variants), and a lack of modularity,
# but as long as you follow the criterion set in locate-test, then the
# end-result for modifying and/or adding tests can be achieved by merely
# rerunning this script.
#
# This script will remain around until (hopefully someday) POSIX make
# becomes less braindead.
#
# See COPYING for more details.
#
# Ngie Cooper, June 2010
#

readonly buildonly_compiler_args="-c"

generate_locate_test_makefile() {

	local maketype=$1; shift

	echo "Generating $maketype Makefiles"

	locate-test --$maketype | sed -e 's,^./,,g' | sort > make-gen.$maketype

	generate_makefiles make-gen.$maketype $*

	rm -f make-gen.$maketype
}

generate_makefile() {

	local link_libs=
	local make_rule_prereq_cache=
	local make_copy_prereq_cache=
	local prereq_cache=
	local tests=
	local targets=

	local makefile=$1
	local prereq_dir=$2
	local compiler_args=$3
	shift 3

	prereq_cache=$*

	test_prefix=$(basename "$prereq_dir")

	# special case for speculative testcases
	if [ "$test_prefix" = "speculative" ]; then
		test_prefix=$(basename $(echo "$prereq_dir" | sed s/speculative//))
		test_prefix="${test_prefix}_speculative"
	fi

	# Add all source files to $make_target_prereq_cache.
	for prereq in $prereq_cache; do
		# Stuff that needs to be tested.
		if echo "$prereq" | grep -Eq '\.(run-test|sh)'; then
			if [ "$tests" != "" ]; then
				tests="$tests "
			fi

			tests="$tests${test_prefix}_$prereq"
		fi

		# Stuff that needs to be compiled.
		if echo "$prereq" | grep -Eq '\.(run-test|sh|test)'; then
			if [ "$targets" != "" ]; then
				targets="$targets "
			fi

			targets="$targets${test_prefix}_$prereq"
		fi

		# Cache for generating compile rules.
		case "$prereq" in
		*.sh)
			# Note that the sh scripts are copied later in order to
			# have the test_prefix as well
			if [ "$make_copy_prereq_cache" != "" ]; then
				make_copy_prereq_cache="$make_copy_prereq_cache "
			fi
			make_copy_prereq_cache="$make_copy_prereq_cache$prereq"
			;;
		*)
			if [ "$make_rule_prereq_cache" != "" ]; then
				make_rule_prereq_cache="$make_rule_prereq_cache "
			fi
			make_rule_prereq_cache="$make_rule_prereq_cache$prereq"
			;;
		esac
	done

	if [ ! -f "$makefile.1" ]; then

		cat > "$makefile.1" <<EOF
#
# Automatically generated by `basename "$0"` -- DO NOT EDIT.
#
# Restrictions for `basename "$0"` apply to this file. See COPYING for
# more details.
#
# $AUTHORDATE
#

# Path variables.
top_srcdir?=		`echo "$prereq_dir" | awk '{ gsub(/[^\/]+/, "..", $0); print }'`
subdir=			$prereq_cache_dir
srcdir=			\$(top_srcdir)/\$(subdir)

include \$(top_srcdir)/include/mk/env.mk

INSTALL_DIR=		\$(DESTDIR)/\$(testdir)/\$(subdir)
LOGFILE?=		logfile

# Build variables
CFLAGS+=		-I\$(top_srcdir)/include

# XXX: for testfrmw.c -- needs to be moved into a library.
CFLAGS+=		-I\$(srcdir)

EOF

		cat >> "$makefile.1" <<EOF
# Submake make definitions.
EOF

		for var in CFLAGS LDFLAGS LDLIBS; do
			if [ -f "${prereq_cache_dir}/$var" ]; then
				cat >> "$makefile.1" <<EOF
${var}+=		`cat "${prereq_cache_dir}/${var}" 2>/dev/null`
EOF
			fi
		done

		# Whitespace
		echo "" >> "$makefile.1"

	fi

	if [ ! -z "${tests}" ]; then
		cat >> "$makefile.2" <<EOF
INSTALL_TARGETS+=	${tests}
EOF
	fi
	cat >> "$makefile.2" <<EOF
MAKE_TARGETS+=		${targets}

ifeq (\$V,1)
VERBOSE=1
endif

ifndef VERBOSE
v=@
endif

EOF

	if [ ! -f "$makefile.3" ]; then

		cat > "$makefile.3" <<EOF
.PHONY: all
all: \$(MAKE_TARGETS)
	@if [ -d speculative ]; then \$(MAKE) -C speculative all; fi

.PHONY: clean
clean:
	rm -f \$(MAKE_TARGETS) logfile* run.sh *.core
	@if [ -d speculative ]; then \$(MAKE) -C speculative clean; fi

.PHONY: install
install: \$(INSTALL_DIR) run.sh
	set -e; for file in \$(INSTALL_TARGETS) run.sh; do	\\
		if [ -f "\$\$file" ] ; then			\\
			install -m 00755 \$\$file		\\
				\$(INSTALL_DIR)/\$\$file;	\\
		fi;						\\
	done
	@if [ -d speculative ]; then \$(MAKE) -C speculative install; fi

.PHONY: test
test: run.sh
	\$(v)./run.sh

\$(INSTALL_DIR):
	mkdir -p \$@

EOF

	fi

	if ! grep -q '^run.sh' "$makefile.3"; then
		cat >> "$makefile.3" <<EOF
run.sh:
	@echo '#!/bin/sh' > \$@
	@echo "\$(top_srcdir)/bin/run-tests.sh \$(subdir) \$(INSTALL_TARGETS)" >> \$@
	@chmod +x run.sh

EOF
	fi

	# Only pass along libraries to link if `-c` was not specified in `$compiler_args`.
	if [ "$compiler_args" = "$buildonly_compiler_args" ]; then
		link_libs=false
	else
		link_libs=true
	fi

	# Produce _awesome_ target rules for everything that needs it.
	for prereq in ${make_rule_prereq_cache}; do

		test_name="$prereq"
		if [ "$suffix" != "" ]; then
			test_name=`echo "$test_name" | sed -e "s,$suffix,,"`
		fi

		c_file="$test_name.c"
		bin_file="${test_prefix}_$prereq"

		case "$suffix" in
		.run-test)
			grep -q 'main' "$prereq_dir/$c_file" || echo >&2 "$prereq_dir/$c_file should be test."
			;;
		.test)
			grep -q 'main' "$prereq_dir/$c_file" && echo >&2 "$prereq_dir/$c_file should be run-test."
			;;
		esac

		COMPILE_STR="\$(CC) $compiler_args \$(CFLAGS) \$(LDFLAGS) -o \$@ \$(srcdir)/$c_file"
		if $link_libs; then
			COMPILE_STR="$COMPILE_STR \$(LDLIBS)"
		fi

		cat >> "$makefile.3" <<EOF
$bin_file: \$(srcdir)/$c_file
	\$(v)if $COMPILE_STR > logfile.\$\$\$\$ 2>&1; then \\
		 cat logfile.\$\$\$\$; \\
		 echo "\$(subdir)/$test_name compile PASSED"; \\
		 echo "\$(subdir)/$test_name compile PASSED" >> \$(LOGFILE); \\
	else \\
		 cat logfile.\$\$\$\$; \\
		 echo "\$(subdir)/$test_name compile FAILED; SKIPPING"; \\
		(echo "\$(subdir)/$test_name compile FAILED; SKIPPING"; cat logfile.\$\$\$\$) >> \$(LOGFILE); \\
	fi; \\
	rm -f logfile.\$\$\$\$

EOF
	done

	# Produce copy rules for .sh scripts.
	for prereq in ${make_copy_prereq_cache}; do
		src="$prereq"
		dst="${test_prefix}_$prereq"

		cat >> "$makefile.3" <<EOF
$dst: \$(srcdir)/$src
	@cp \$(srcdir)/$src \$@

EOF
	done
}

generate_makefiles() {

	local prereq_cache=

	local make_gen_list=$1
	local suffix=$2
	local compiler_args="$3"

	while read filename; do

		prereq_dir=`dirname "$filename"`

		# First run.
		if [ "$prereq_cache_dir" = "" ] ; then
			prereq_cache_dir="$prereq_dir"
		elif [ "$prereq_cache_dir" != "$prereq_dir" ]; then

			generate_makefile "$prereq_cache_dir/Makefile" "$prereq_cache_dir" "$compiler_args" $prereq_cache

			# Prep for the next round..
			prereq_cache=
			prereq_cache_dir="$prereq_dir"

		fi

		# Cache the entries to punt out all of the data at
		# once for a single Makefile.
		if [ "$prereq_cache" != "" ] ; then
			prereq_cache="$prereq_cache "
		fi
		prereq_cache="$prereq_cache"`basename "$filename" | sed "s,.c\$,$suffix,g"`

	done < $make_gen_list

	# Dump the last Makefile data cached up.
	generate_makefile "$prereq_cache_dir/Makefile" $prereq_cache_dir "$compiler_args" $prereq_cache

}

export PATH="$PATH:`dirname "$0"`"

AUTHORDATE=`grep "Ngie Cooper" "$0" | head -n 1 | sed 's,# *,,'`
TOP_SRCDIR=${TOP_SRCDIR:=`dirname "$0"`/..}

# For the generic cases.
generate_locate_test_makefile buildonly '.test' "$buildonly_compiler_args"
generate_locate_test_makefile runnable '.run-test'
generate_locate_test_makefile test-tools ''

find . -name Makefile.1 -exec dirname {} \; | while read dir; do
	if [ -f "$dir/Makefile.2" ]; then
		cat $dir/Makefile.1 $dir/Makefile.2 $dir/Makefile.3 > $dir/Makefile
	fi
	rm $dir/Makefile.1 $dir/Makefile.2 $dir/Makefile.3
done
