# Source configuration.
include Makefile.conf

SHELL := /bin/sh

# Executables configuration.
subdirs := train predict kfold
programs := $(subdirs)
progpaths := $(foreach d,$(programs),$(PROGDIR)/$(d))

# Executable and objects.
.PHONY : all test doc clean cleanall release

all:
	@if [ ! -d programs ]; then mkdir programs; fi
	@$(RM) -f programs/*
	@for d in $(subdirs); do \
		if ! $(MAKE) -C $$d; then \
			echo "Aborting: failed to make $$d"; \
			exit 1; \
		else \
			$(CP) $$d/$$d programs; \
		fi; \
	done

test: all
	$(MAKE) -C testing pd="$(PWD)"/programs

doc:
	$(MAKE) -C doc

valgrind:
	$(MAKE) -C testing pd="$(PWD)"/programs valgrind


###########
# RELEASE #
###########

.PHONY : release
release:
	@if [ -z "$(d)" ]; then \
		echo; \
		echo "Error: d=<rel_dir> not specified on command-line."; \
		exit 1; \
	fi
	@if [ -d "$(d)" ]; then \
		echo; \
		echo "Error: directory $(d) already exists."; \
		exit 1; \
	fi

# Make a good test, then clean up extra test files.
	$(MAKE) cleanall
	$(MAKE) test
	$(MAKE) all
	$(MAKE) cleanall

# Make docs, then clean up LaTeX and BibTeX side-product files.
	$(MAKE) doc
	$(MAKE) -C doc clean

# We now have binaries, sources, and docs.  Copy these to new
# directory.
	mkdir "$(d)"
	$(CP) -dpR * "$(d)"/

# Remove CVS stuff.
	$(RM)    "$(d)"/CVS/*
	$(RMDIR) "$(d)"/CVS
	$(RM)    "$(d)"/*/CVS/*
	$(RMDIR) "$(d)"/*/CVS

# Remove other stuff.
	$(RM)    "$(d)"/package_info.txt

# Set version.
	$(DATE) > "$(d)"/VERSION


###################
# CALLED/UNCALLED #
###################

.PHONY : called uncalled
# Create testing/called.txt, a file which lists (roughly) which
# functions were called during execution of our four testing scripts.
# I have observed some strange behavior in the gprof call graphs that
# makes me wonder how reliable this function-call data is.  That said,
# for my purposes (cleaning up old code), this is good enough.
called: testing/called.txt
testing/called.txt:
	if [ -z "$(PROF)" -o -z "$(GMON)" ]; then \
		echo "PROF and GMON are not defined in Makefile.inc"; \
		exit 1; \
	fi
# clean and rebuild to guarantee all libs and executables have profiling
	$(MAKE) cleanall
	$(MAKE) t=profile
# Profile in several ways, gathering all called functions
# We could arrange to do all of this through stdout and not create
# any files except $(GMON), but this would make this makefile look
# much more messy.
# train.csv
	$(MAKE) -C testing pd="$(PWD)"/programs test.train.csv
	$(PROF) --no-graph programs/train testing/$(GMON) \
		| testing/stripprof.sh > testing/called.train.csv
# predict.csv
	$(MAKE) -C testing pd="$(PWD)"/programs test.predict.csv
	$(PROF) --no-graph programs/predict testing/$(GMON) \
		| testing/stripprof.sh > testing/called.predict.csv
# train.spardat
	$(MAKE) -C testing pd="$(PWD)"/programs test.train.spardat
	$(PROF) --no-graph programs/train testing/$(GMON) \
		| testing/stripprof.sh > testing/called.train.spardat
# predict.spardat
	$(MAKE) -C testing pd="$(PWD)"/programs test.predict.spardat
	$(PROF) --no-graph programs/predict testing/$(GMON) \
		| testing/stripprof.sh > testing/called.predict.spardat
# take the union of the lists of called functions
	$(CAT) testing/called.train.csv testing/called.predict.csv \
		testing/called.train.spardat testing/called.predict.spardat \
	| sort | uniq > testing/called.txt
# clean
	$(RM) testing/$(GMON)
	$(RM) testing/called.train.csv
	$(RM) testing/called.predict.csv
	$(RM) testing/called.train.spardat
	$(RM) testing/called.predict.spardat


# Create testing/uncalled.txt, a file which lists (roughly) which
# functions were never called during execution of our four testing
# scripts.  I have observed some strange behavior in the gprof call
# graphs that makes me wonder how reliable this function-call data is.
# That said, for my purposes (cleaning up old code), this is good
# enough.
uncalled: testing/uncalled.txt
testing/uncalled.txt:
	if [ -z "$(PROF)" -o -z "$(GMON)" ]; then \
		echo "PROF and GMON are not defined in Makefile.inc"; \
		exit 1; \
	fi
# clean and rebuild to guarantee all libs and executables have profiling
	$(MAKE) cleanall
	$(MAKE) t=profile
# Profile in several ways, gathering all uncalled functions
# We could arrange to do all of this through stdout and not create
# any files except $(GMON), but this would make this makefile look
# much more messy.
# train.csv
	$(MAKE) -C testing pd="$(PWD)"/programs test.train.csv
	$(PROF) --no-graph -z -c programs/train testing/$(GMON) \
		| testing/stripcalled.sh > testing/uncalled.train.csv
# predict.csv
	$(MAKE) -C testing pd="$(PWD)"/programs test.predict.csv
	$(PROF) --no-graph -z -c programs/predict testing/$(GMON) \
		| testing/stripcalled.sh > testing/uncalled.predict.csv
# train.spardat
	$(MAKE) -C testing pd="$(PWD)"/programs test.train.spardat
	$(PROF) --no-graph -z -c programs/train testing/$(GMON) \
		| testing/stripcalled.sh > testing/uncalled.train.spardat
# predict.spardat
	$(MAKE) -C testing pd="$(PWD)"/programs test.predict.spardat
	$(PROF) --no-graph -z -c programs/predict testing/$(GMON) \
		| testing/stripcalled.sh > testing/uncalled.predict.spardat
# take the intersection of the lists of uncalled functions
	$(CAT) testing/uncalled.train.csv \
	| $(FGREP) -x -f testing/uncalled.predict.csv \
	| $(FGREP) -x -f testing/uncalled.train.spardat \
	| $(FGREP) -x -f testing/uncalled.predict.spardat \
	| sort | uniq > testing/uncalled.txt
# clean
	$(RM) testing/$(GMON)
	$(RM) testing/uncalled.train.csv
	$(RM) testing/uncalled.predict.csv
	$(RM) testing/uncalled.train.spardat
	$(RM) testing/uncalled.predict.spardat


.PHONY : called.file.% uncalled.file.%
called.file.%: testing/called.txt
# Get functions for file ($*, the part that matches % in this rule)
# and check them against testing/called.txt.  If the file isn't in the
# current directory, the first matching file in subdirectories (in
# wildcard order, some sort of alphabetical) is used.
	@fname="$*"; \
	if [ ! -r "$$fname" ]; then \
		fname=$$(echo */"$$fname" | $(CUT) -f1 -d' '); \
	fi; \
	result=$$($(CTAGS) -x "$$fname" | $(CUT) -f1 -d' ' \
		| $(FGREP) -x -f - testing/called.txt); \
	for r in $$result; do \
		echo "$$r"; \
	done

uncalled.file.%: testing/uncalled.txt
# Get functions for file ($*, the part that matches % in this rule)
# and check them against testing/uncalled.txt.  If the file isn't in
# the current directory, the first matching file in subdirectories (in
# wildcard order, some sort of alphabetical) is used.
	@fname="$*"; \
	if [ ! -r "$$fname" ]; then \
		fname=$$(echo */"$$fname" | $(CUT) -f1 -d' '); \
	fi; \
	result=$$($(CTAGS) -x "$$fname" | $(CUT) -f1 -d' ' \
		| $(FGREP) -x -f - testing/uncalled.txt); \
	for r in $$result; do \
		echo "$$r"; \
	done


# Clean.
.PHONY : clean cleanall
clean:
	$(RM) *~

cleanall: clean
	for d in $(subdirs); do make -C "$$d" cleanall; done
	$(RM) testing/called.txt testing/uncalled.txt
	$(RM) $(progpaths)
	$(MAKE) -C testing pd="$(PWD)"/"$(PROGDIR)" clean
	$(MAKE) -C doc cleanall

