VERSION=0.6

BIN_OPT:= \
  create-graph.native \
  print-stats.native \
  bin2src.native \
  src2bin.native \
  build-fixpoint.native \
  build-closure.native \
  clean-repository.native \
  buildgraph2srcgraph.native \
  annotate-strong.native \
  partial-order.native \
  find-fvs.native \
  collapse-srcgraph.native \
  optuniv.native \
  calculate-fas.native \
  buildcheck-more-problems.native \
  distcheck-more-problems.native

BIN_BYTE:=$(patsubst %.native, %.d.byte, $(BIN_OPT))
BIN_PROF:=$(patsubst %.native, %.p.native, $(BIN_OPT))

BIN_PYTOOLS:=$(patsubst tools/%.py, %, $(filter-out tools/util.py,$(wildcard tools/*.py)))
BIN_OCTOOLS:=$(patsubst %.native, %, $(BIN_OPT))
BIN_SHTOOLS:=$(patsubst tools/%.sh, %, $(wildcard tools/*.sh))

BIN_TOOLS:=$(BIN_PYTOOLS) $(BIN_OCTOOLS) $(BIN_SHTOOLS)
MANPAGES:=$(patsubst %, doc/man/botch-%.1, $(BIN_TOOLS))

OCAML_BEST ?= $(if $(wildcard /usr/bin/ocamlopt),native,d.byte)

PWD := $(shell pwd)
BUILD = $(PWD)/dose/_build
DOSELIBS = $(PWD)/dose/_build/dose3
CUDFLIBS = $(PWD)/dose/_build/cudf
DOSEBYTELIBS = common/common.cma deb/debian.cma rpm/rpm.cma opencsw/csw.cma eclipse/eclipse.cma algo/algo.cma doseparse/boilerplateNoRpm.cma doseparse/boilerplate.cma
CFLAGS=-cflags "-w +a-4-9"
bindir=/usr/bin
datadir=/usr/share/botch

# we have to export PYTHONHASHSEED because otherwise networkx will create
# content with random output order
#   https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=749710
#   https://github.com/networkx/networkx/issues/1181
# on the other hand, the generated hashes are not the same on 32 vs 64 arches
#   http://bugs.python.org/issue22621
#   http://bugs.debian.org/778373
export PYTHONHASHSEED=0
# to make the sort order in the tests locale independent
export LC_COLLATE=C
# because dose2html outputs utf8 on standard output
export PYTHONIOENCODING=utf-8

.PHONY: all
all: $(OCAML_BEST) doc

.PHONY: native
native: atdgen
	OCAMLPATH=$(BUILD) ocamlbuild -classic-display -use-ocamlfind $(CFLAGS) $(BIN_OPT)

.PHONY: d.byte
d.byte: atdgen
	OCAMLPATH=$(BUILD) ocamlbuild -classic-display -use-ocamlfind $(CFLAGS) $(BIN_BYTE)

# profiling for use of native executables with gprof
.PHONY: profile
profile: atdgen
	OCAMLPATH=$(BUILD) ocamlbuild -classic-display -use-ocamlfind $(CFLAGS) $(BIN_PROF)

doc/man/botch.1: $(MANPAGES)
	./doc/man/generate-botch-manpage.py | pod2man --section 1 --center="botch tools" --name botch > $@

.PHONY: doc
doc: $(MANPAGES) doc/man/botch.1
	$(MAKE) -C doc/wiki

doc/man/%.1: doc/man/%.pod
	pod2man --section 1 --center="botch tools" $< > $@

.PHONY: doselib
doselib:
	(cd dose && ./configure --with-zip --with-bz2 --with-ocamlgraph --without-libcudf && make clean fast $(DOSEBYTELIBS))
	rm -Rf $(DOSELIBS)
	mkdir -p $(DOSELIBS)
	cp dose/META $(DOSELIBS)
	for i in dose/_build/*/common.* \
		dose/_build/*/debian.* \
		dose/_build/*/algo.* \
		dose/_build/*/csw.* \
		dose/_build/*/eclipse.* \
		dose/_build/*/boilerplate.* \
		dose/_build/*/boilerplateNoRpm.*; do \
	  if [ -e $$i ]; then \
	  cp $$i $(DOSELIBS) ; \
	  rm -f $(DOSELIBS)/*.mlpack $(DOSELIBS)/*.cmx ; \
	  fi ; \
	done
	rm -Rf $(CUDFLIBS)
	mkdir -p $(CUDFLIBS)
	cp dose/cudf/META $(CUDFLIBS)
	cp dose/_build/doselibs/cudf*.cmi $(CUDFLIBS)
	for i in dose/_build/doselibs/cudf.*; do \
	  if [ -e $$i ]; then \
	    cp $$i $(CUDFLIBS) ; \
	    rm -f $(CUDFLIBS)/*.mlpack $(CUDFLIBS)/*.cmx ; \
	  fi ; \
	done

%_j.ml: %.atd
	atdgen -j -j-std $<

%_t.ml: %.atd
	atdgen -t $<

.PHONY: atdgen
atdgen: datatypes_j.ml datatypes_t.ml

.PHONY: test-default
test-default: all
	rm -rf tmp out
	./tools/native.sh --debug --verbose --develop --output out --tmp tmp amd64 tests/sid-amd64-packages-20140101T000000Z tests/sid-sources-20140101T000000Z
	for f in tests/default/tmp/* tmp/*; do basename "$$f"; done | sort | uniq | while read f; do \
		echo checking $$f; \
		case "$$f" in \
			*.xml) ./tools/graph-difference.py "tests/default/tmp/$$f" "tmp/$$f" || exit 1;; \
			*) diff -u "tests/default/tmp/$$f" "tmp/$$f" || exit 1;; \
		esac; \
	done
	for f in tests/default/out/* out/*; do basename "$$f"; done | sort | uniq | while read f; do \
		echo checking $$f; \
		case "$$f" in \
			*.xml) ./tools/graph-difference.py "tests/default/out/$$f" "out/$$f" || exit 1;; \
			*) diff -u "tests/default/out/$$f" "out/$$f" || exit 1;; \
		esac; \
	done

.PHONY: test-selfcontained
test-selfcontained: all
	rm -rf tmp out
	./tools/native.sh --debug --verbose --develop --output out --tmp tmp --latest --clean --self-contained --optuniv --sapsb --strong --no-drop amd64 tests/sid-amd64-packages-20140101T000000Z tests/sid-sources-20140101T000000Z
	for f in tests/selfcontained/tmp/* tmp/*; do basename "$$f"; done | sort | uniq | while read f; do \
		echo checking $$f; \
		case "$$f" in \
			*.xml) ./tools/graph-difference.py "tests/selfcontained/tmp/$$f" "tmp/$$f" || exit 1;; \
			*) diff -u "tests/selfcontained/tmp/$$f" "tmp/$$f" || exit 1;; \
		esac; \
	done
	for f in tests/selfcontained/out/* out/*; do basename "$$f"; done | sort | uniq | while read f; do \
		echo checking $$f; \
		case "$$f" in \
			*.xml) ./tools/graph-difference.py "tests/selfcontained/out/$$f" "out/$$f" || exit 1;; \
			*) diff -u "tests/selfcontained/out/$$f" "out/$$f" || exit 1;; \
		esac; \
	done

.PHONY: test-cross
test-cross: all
	rm -rf tmp out
	mkdir out
	cp tests/cross-ma.diff out/ma.diff
	./tools/cross.sh --debug --verbose --develop --output=out --tmp=tmp amd64 armhf tests/sid-amd64-packages-20140101T000000Z tests/sid-sources-20140101T000000Z
	for f in tests/cross/tmp/* tmp/*; do basename "$$f"; done | sort | uniq | while read f; do \
		echo checking $$f; \
		case "$$f" in \
			*.xml) ./tools/graph-difference.py "tests/cross/tmp/$$f" "tmp/$$f" || exit 1;; \
			*) diff -u "tests/cross/tmp/$$f" "tmp/$$f" || exit 1;; \
		esac; \
	done
	for f in tests/cross/out/* out/*; do basename "$$f"; done | sort | uniq | while read f; do \
		echo checking $$f; \
		case "$$f" in \
			*.xml) ./tools/graph-difference.py "tests/cross/out/$$f" "out/$$f" || exit 1;; \
			*) diff -u "tests/cross/out/$$f" "out/$$f" || exit 1;; \
		esac; \
	done

.PHONY: test
test: test-default test-selfcontained test-cross
	# test whether there is no manpage without a program by
	# normalizing program and manpage names and only printing
	# those names which are not duplicate but unique
	ls tools/*.py *.$(OCAML_BEST) tools/*.sh doc/man/botch-*.pod \
		| grep -v tools/util.py \
		| while read pkg; do \
			pkg=`basename $$pkg .$(OCAML_BEST)`; \
			pkg=`basename $$pkg .py`; \
			pkg=`basename $$pkg .sh`; \
			pkg=`basename $$pkg .pod`; \
			echo $${pkg#botch-}; \
		done | sort | uniq -u | while read pkg; do \
			echo doc/man/botch-$${pkg}.pod is superfluous >&2; \
			exit 1; \
		done
	# FIXME: add more tests
	#./tests.py
	#OCAMLPATH=$(BUILD) ocamlbuild -classic-display -use-ocamlfind $(CFLAGS) tests.$(OCAML_BEST)
	#./tests.$(OCAML_BEST)
	pyflakes3 tools/*.py
	pep8 tools/*.py

.PHONY: install
install: all
	$(MAKE) -C doc/wiki install
	mkdir -p $(DESTDIR)$(bindir)
	for octool in $(BIN_OCTOOLS); do \
		cp _build/$$octool.$(OCAML_BEST) $(DESTDIR)$(bindir)/botch-$$octool; \
	done
	for shtool in $(BIN_SHTOOLS); do \
		cp tools/$$shtool.sh $(DESTDIR)$(bindir)/botch-$$shtool; \
	done
	for pytool in $(BIN_PYTOOLS); do \
		cp tools/$$pytool.py $(DESTDIR)$(bindir)/botch-$$pytool; \
	done
	mkdir -p $(DESTDIR)$(datadir)
	cp -r droppable $(DESTDIR)$(datadir)
	cp tools/util.py $(DESTDIR)$(datadir)

.PHONY: clean
clean:
	ocamlbuild -clean
	rm -f datatypes_j.ml datatypes_j.mli datatypes.ml datatypes.mli datatypes_t.ml datatypes_t.mli
	rm -f tools/*.pyc
	rm -f *.native *.d.byte
	rm -f doc/man/*.1
	rm -rf tools/__pycache__ tmp out
	$(MAKE) -C doc/wiki clean

.PHONY: doseclean
doseclean:
	(cd dose && ocamlbuild -clean)

.PHONY: tarball
tarball:
	$(eval tmpdir := $(shell mktemp --directory))
	git archive --worktree-attributes --prefix=botch-$(VERSION)/ -o $(tmpdir)/botch-$(VERSION).tar HEAD
	git -C doc/wiki archive --worktree-attributes --prefix=botch-$(VERSION)/doc/wiki/ -o $(tmpdir)/botch-doc.tar HEAD
	git -C tests archive --worktree-attributes --prefix=botch-$(VERSION)/tests/ -o $(tmpdir)/botch-tests.tar HEAD
	# tar --concatenate seems to only take two files as input so we can
	# neither replace the temporary files with a pipe nor combine both of
	# the below commands into one... :(
	tar --concatenate -f $(tmpdir)/botch-$(VERSION).tar $(tmpdir)/botch-doc.tar
	tar --concatenate -f $(tmpdir)/botch-$(VERSION).tar $(tmpdir)/botch-tests.tar
	# testing shows that in this case, -9e compresses better than -9
	xz --verbose -c9e $(tmpdir)/botch-$(VERSION).tar > ../botch-$(VERSION).tar.xz
	rm -rf $(tmpdir)

.PHONY: upload
upload: tarball
	gpg -u 8FBD83E1 --armor --detach-sig ../botch-$(VERSION).tar.xz
	scp ../botch-$(VERSION).tar.xz.asc ../botch-$(VERSION).tar.xz mmfn:/var/www/botch/
	ssh mmfn tree -s --noreport -P "botch-*.tar.xz*" -o /var/www/botch/index.html -v -L 1 -H "." -T "botch-releases" /var/www/botch/
