#!/usr/bin/make -f

#
# CONTENTS:
#
# 1. DEFINITIONS
# 2. GENERATE FILES
#    Targets to auto-generate files
# 3. PACKAGE BUILD
#    Mostly standard targets that are used during package build
# 4. TEST SUITE
# 5. MAINTAINER TARGETS
#    Convenience targets to be used by maintainers
#    Some are explained at https://wiki.debian.org/DebianScience/Sage
#

###############################################################################
# 1. DEFINITIONS
###############################################################################

include /usr/share/dpkg/pkg-info.mk
include /usr/share/dpkg/architecture.mk

MAX_TEST_FAILURES = 50
MAX_TEST_FAILURES_RERUN = $(shell expr 2 \* $(MAX_TEST_FAILURES))

# If we're doing a pre-release, then be less strict about test failures, etc
ifneq (,$(filter $(DEB_DISTRIBUTION),UNRELEASED unstable-sage))
is_prerelease = true
else
is_prerelease = false
endif

# Some buildds are a bit slow, see https://buildd.debian.org/status/logs.php?pkg=gcc-7 for a fuller list
ifeq (false,$(is_prerelease))
ifneq (,$(filter $(DEB_BUILD_ARCH),armel armhf mips mipsel mips64el))
export SAGE_TIMEOUT_LONG = 7200
else
export SAGE_TIMEOUT_LONG = 3600
endif
endif

# Use ccache if pkg.sagemath.ccache build-profile is active.
# Your rebuilds will be *much quicker*.
ifneq (,$(filter pkg.sagemath.ccache,$(DEB_BUILD_PROFILES)))
export PATH := /usr/lib/ccache:$(PATH)
export CCACHE_DIR := $(CURDIR)/debian/ccache
export CCACHE_BASEDIR := $(CURDIR)/debian/build
$(shell mkdir -p "$(CCACHE_DIR)")
endif

ifeq (,$(filter pkg.sagemath.nolongcheck,$(DEB_BUILD_PROFILES)))
SAGE_TEST_FLAGS = --long
endif

export SAGE_LOCAL = $(CURDIR)/debian/build/usr
export PYTHONPATH = $(CURDIR)/debian/build/usr/lib/python2.7/dist-packages
export DOT_SAGE = $(CURDIR)/debian/test
export SAGE_SCRIPTS_DIR = $(CURDIR)/sage/src/bin

LANGS = $(shell cd sage/src/doc && find . -mindepth 1 -maxdepth 1 -type d | grep -v common | cut -b3- | LC_ALL=C.UTF-8 LANG=C.UTF-8 sort)
DOCS_INSTALL = $(LANGS:%=debian/sagemath-doc-%.install)
DOCS_CONTROL = $(LANGS:%=debian/sagemath-doc-%.control)
DOCS_DOC_BASE = $(LANGS:%=debian/sagemath-doc-%.doc-base)
DOCS_OVERRIDES = $(LANGS:%=debian/sagemath-doc-%.lintian-overrides)
DOCS_INSTALL_DIRS = $(LANGS:%=debian/sagemath-doc-%)

ifneq (,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
NUMJOBS = $(patsubst parallel=%,%,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
else
NUMJOBS = 1
endif

ifneq (1,$(TRY_AVOID_REBUILD))
REBUILD_RULES = debian/rules
endif

%:
	dh $@ --with=python2,sphinxdoc

###############################################################################
# 2. GENERATE FILES
###############################################################################

binary build clean install: debian/control

debian/control: debian/control.in $(DOCS_CONTROL)
	cat $^ > "$@"
	sed -i -e '/RUNTIME_DEPENDS/ {' -e 'r debian/control.runtime-depends' -e 'd' -e '}' "$@"
	sed -i -e '/JUPYTER_DEPENDS/ {' -e 'r debian/control.jupyter-depends' -e 'd' -e '}' "$@"

debian/sagemath-doc-%.control: debian/sagemath-doc-LANG.control.in $(REBUILD_RULES)
	sed -e "s/LANGUAGE/$$(debian/lang.py $*)/g" -e 's/LANG/$*/g' < "$<" > "$@"

debian/sagemath-doc-%.install: debian/sagemath-doc-LANG.install.in $(REBUILD_RULES)
	sed -e "s/LANGUAGE/$$(debian/lang.py $*)/g" -e 's/LANG/$*/g' < "$<" > "$@"

debian/sagemath-doc-en.doc-base: debian/sagemath-doc-LANG.doc-base.in $(REBUILD_RULES)
	sed -e "s/LANGUAGE/$$(debian/lang.py en)/g" -e 's/LANG/en/g' -e 's/SUBDIR//g' < "$<" > "$@"

debian/sagemath-doc-ca.doc-base: debian/sagemath-doc-LANG.doc-base.in $(REBUILD_RULES)
	sed -e "s/LANGUAGE/$$(debian/lang.py ca)/g" -e 's/LANG/ca/g' -e 's/SUBDIR/intro\//g' < "$<" > "$@"

debian/sagemath-doc-%.doc-base: debian/sagemath-doc-LANG.doc-base.in $(REBUILD_RULES)
	if [ "$*" = "hu" -o "$*" = "it" -o "$*" = "tr" ]; then \
		sed -e "s/LANGUAGE/$$(debian/lang.py $*)/g" -e 's/LANG/$*/g' -e 's/SUBDIR/a_tour_of_sage\//g' < "$<" > "$@"; \
	else \
		sed -e "s/LANGUAGE/$$(debian/lang.py $*)/g" -e 's/LANG/$*/g' -e 's/SUBDIR/tutorial\//g' < "$<" > "$@"; \
	fi

debian/sagemath-doc-%.lintian-overrides: debian/sagemath-doc-LANG.lintian-overrides.in \
  $(wildcard debian/sagemath-doc-%.lintian-overrides.in) $(REBUILD_RULES)
	sed -e "s/LANGUAGE/$$(debian/lang.py $*)/g" -e 's/LANG/$*/g' < "$<" > "$@"
	if [ -f "debian/sagemath-doc-$*.lintian-overrides.in" ]; then \
		sed -e "s/LANGUAGE/$$(debian/lang.py $*)/g" -e 's/LANG/$*/g' < "debian/sagemath-doc-$*.lintian-overrides.in" >> "$@"; \
	fi

../sagemath_$(DEB_VERSION_UPSTREAM).orig.tar.xz: .git/modules/sage/HEAD
	cd sage && git archive --prefix=sagemath_$(DEB_VERSION_UPSTREAM)/sage/ \
	  --format=tar HEAD | xz -zf > ../../sagemath_$(DEB_VERSION_UPSTREAM).orig.tar.xz

../sagemath_$(DEB_VERSION).dsc: distclean-sage distclean ../sagemath_$(DEB_VERSION_UPSTREAM).orig.tar.xz debian/control
	dpkg-buildpackage -nc -d -uc -us -S

###############################################################################
# 3. PACKAGE BUILD
###############################################################################

prune: debian/control
	cd debian/pruner && autoconf -I m4
	cd debian/pruner && ./configure --with-sage-root="$(CURDIR)/sage"
	cd sage && python ../debian/pruner/pruner.py || $(is_prerelease)
	touch prune

run_build = \
	MAKE='make -j$(NUMJOBS)' PREREQ_OPTIONS="--prefix=$(SAGE_LOCAL)" \
	  $(MAKE) --directory=sage

override_dh_auto_configure: prune

override_dh_auto_build-arch:
	$(run_build) build
	cd sage && src/bin/sage-starts

override_dh_auto_build-indep:
ifeq (,$(filter nodoc,$(DEB_BUILD_OPTIONS)))
	$(run_build) doc
endif

override_dh_auto_install:
	rm -rf debian/build/usr/var/tmp/sage/build
# We built .pyo to make the docbuild/tests go faster, but as per Debian policy
# we don't distribute them.
	find debian/build '(' -name '*.pyc' -o -name '*.pyo' ')' -delete
	rm -f debian/build/usr/bin/*.bat
	mkdir -p debian/tmp
	mv debian/build/* debian/tmp
	cp -f debian/adhoc/sage-env debian/tmp/usr/bin/
# Don't try to rmdir debian/build if it's a mountpoint, useful for doing builds in /run/shm
	mountpoint debian/build || rmdir debian/build

# If we see "nodoc", don't clean the docs. This allows us to rebuild just the
# non-docs without wiping away the docs we previously built, to save time.
override_dh_auto_clean:
ifneq (,$(filter nodoc,$(DEB_BUILD_OPTIONS)))
	dh_auto_clean -Dsage -- -k clean sagelib-clean misc-clean bootstrap-clean || true
	rm -rf sage/local sage/upstream
else
	dh_auto_clean -Dsage -- maintainer-clean
endif

override_dh_install-indep: $(DOCS_INSTALL) $(DOCS_DOC_BASE) $(DOCS_OVERRIDES)
	dh_install --package sagemath-common -X.so
	dh_install --remaining-packages --list-missing
# Basic version of deduplicating the docs, for a much smaller install size.
# Upstream bug report is at https://trac.sagemath.org/ticket/22088
# Hard links do work in Debian packages, but only within packages.
	set -e; for d in $(DOCS_INSTALL_DIRS); do \
		rdfind -outputname /dev/null -makehardlinks true "$$d"; \
	done

override_dh_python2-arch:
	dh_python2
	dh_numpy --package sagemath # stop lintian complaining at us

override_dh_shlibdeps:
	dh_shlibdeps -l debian/tmp/usr/lib

override_dh_compress:
# We probably don't need to install the pickle/doctree files but let's exempt
# them from compression anyway for now, so the build goes quicker.
	dh_compress -X.pdf -X.pickle -X.doctree

override_dh_sphinxdoc:
# TODO: fix MathJax.js, Sage needs special treatment
# `man dh_sphinxdoc` says symlinking translations.js is not yet supported
# likewise, it seems not to recognise searchtools.js yet
	dh_sphinxdoc -XMathJax.js -Xtranslations.js -Xsearchtools.js

# If we see "nodoc", don't clean the docs. This allows us to rebuild just the
# non-docs without wiping away the docs we previously built, to save time.
ifneq (,$(filter nodoc,$(DEB_BUILD_OPTIONS)))
# Don't delete my precious docs debhelper! They took a whole fucking hour to build!
preserve_docs = \
	set -e; \
	if [ -d $(2)/usr/share/doc ]; then \
		mkdir -p debian/clean-tmp/usr/share; \
		mv -t debian/clean-tmp/usr/share/ $(2)/usr/share/doc; \
	fi; \
	$(1); \
	if [ -d debian/clean-tmp/usr/share/doc ]; then \
		mkdir -p $(2)/usr/share; \
		mv -t $(2)/usr/share/ debian/clean-tmp/usr/share/doc; \
		rm -rf debian/clean-tmp; \
	fi
else
preserve_docs = $(1)
endif
override_dh_clean:
	rm -f $(DOCS_INSTALL) $(DOCS_DOC_BASE) $(DOCS_OVERRIDES)
	rm -rf $(DOT_SAGE)
	$(call preserve_docs,dh_clean,debian/tmp)
	$(call preserve_docs,rm -rf debian/build/*,debian/build)

###############################################################################
# 4. TEST SUITE
###############################################################################

TESTS_MK = $(MAKE) -s --no-print-directory -f debian/tests.mk LOGFILE=$(LOGFILE)
failed-tests%:
	@$(TESTS_MK) "$@"

# We used to set this for i386 but it seems to be OK now.
# Perhaps we should set it for armhf, let's see the buildd test results...
ifneq (,$(filter $(DEB_BUILD_ARCH),))
IGNORE_FAILURES = | sed -e '0,/Timed out/{//d;}' -e '0,/Timed out/{//d;}' -e '0,/Timed out/{//d;}'
else
IGNORE_FAILURES =
endif
had-few-failures:
	if ! test -f $(LOGFILE); then echo "Error: log file $(LOGFILE) not found"; false; fi
	N_TEST_FAILURES="$$($(TESTS_MK) failed-tests-total-normal)"; \
	  if ! test $${N_TEST_FAILURES} -le $(MAX_TEST_FAILURES); then \
	    echo "Error: $${N_TEST_FAILURES} tests failed, up to $(MAX_TEST_FAILURES) failures are tolerated"; \
	    false; \
	  else \
	    echo "Success: $${N_TEST_FAILURES} tests failed, up to $(MAX_TEST_FAILURES) failures are tolerated"; \
	  fi
	if ! test -z "$$($(TESTS_MK) failed-tests-special $(IGNORE_FAILURES))"; then \
	  echo "Error: critical test failures (e.g. timeout, segfault, etc.)"; false; fi

had-not-too-many-failures:
	echo "Checking number of failed tests to determine whether to rerun tests in series..."
	if ! test -f $(LOGFILE); then echo "Error: log file $(LOGFILE) not found"; false; fi
	N_TEST_FAILURES="$$($(TESTS_MK) failed-tests-total-normal)"; \
	  if ! test $${N_TEST_FAILURES} -le $(MAX_TEST_FAILURES_RERUN); then \
	    echo "No: $${N_TEST_FAILURES} tests failed, up to $(MAX_TEST_FAILURES_RERUN) failures are tolerated for rerun"; \
	    false; \
	  else \
	    echo "Yes: $${N_TEST_FAILURES} tests failed, up to $(MAX_TEST_FAILURES_RERUN) failures are tolerated for rerun"; \
	  fi

run_tests = \
	cd sage && ./sage -t -p $(NUMJOBS) $(SAGE_TEST_FLAGS) --logfile=logs/ptestlong-$(1).log $(2)
# If tests fail but not by too many more, then retry them once not in parallel
# i386 seems to have issues running parallel tests - TODO: investigate this in more detail
check_test_log = debian/rules -s LOGFILE=sage/logs/ptestlong-$(1).log
run_tests_with_retry = \
	$(run_tests); cd "$(CURDIR)" && \
	if $(is_prerelease); then :; \
	elif $(check_test_log) had-few-failures; then :; else \
	  $(check_test_log) had-not-too-many-failures && \
	  mv sage/logs/ptestlong-$(1).log sage/logs/ptestlong-$(1).log.1 && \
	  { $(run_tests) -p 1 -f; cd "$(CURDIR)" && \
	  $(check_test_log) had-few-failures; }; \
	fi

override_dh_auto_test-arch:
ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
	# TODO: exclude src/sage/misc/sagedoc.py, this will cause ~7 failures if we don't build the docs
	$(call run_tests_with_retry,arch,src/sage /usr/lib/python2.7/dist-packages/sagenb/)
endif

override_dh_auto_test-indep:
ifeq (,$(filter nodoc,$(DEB_BUILD_OPTIONS)))
ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
	# These tests never fail so don't bother with the retry logic
	$(call run_tests,indep,src/doc src/sage/misc/sagedoc.py)
	# Sometimes, parts of the docbuild fail with MemoryError or OSError but
	# Sage doesn't detect this. Here we detect it and fail the build if so.
	# Also filter out some false positives that are not hard errors.
	! (grep Error sage/logs/dochtml.log | grep -v "citation not found: .*Error\|Citation \[.*Error\] is not referenced")
endif
endif

# manual target to re-run only failed tests
check-failed:
	$(call run_tests,all,--all -f)

###############################################################################
# 5. MAINTAINER TARGETS
###############################################################################

export-build-env:
	@echo export SAGE_LOCAL=$(SAGE_LOCAL)
	@echo export PYTHONPATH=$(PYTHONPATH)
	@echo export DOT_SAGE=$(DOT_SAGE)
	@echo export SAGE_SCRIPTS_DIR=$(SAGE_SCRIPTS_DIR)

# If the docbuild fails and you want to try again, you should run this first.
# Otherwise the docbuild gets slower and slower as it re-reads information from
# its build directory. At least that's my (infinity0) impression; I didn't
# investigate this in too much detail yet.
clean-docbuild:
	rm -rf debian/build/usr/share/doc/sagemath

distclean:
	# don't git reset --hard on purpose, so it's easier to test
	git clean -fdx

distclean-sage:
	cd sage; git clean -fdx && git reset --hard HEAD && git submodule update --force

reset: clean clean-docbuild
	QUILT_PATCHES=debian/patches quilt pop -af || true
	debian/rules distclean-sage
	QUILT_PATCHES=debian/patches quilt push -a

build-dep-maint:
	sudo apt-get install --no-install-recommends equivs devscripts git iso-codes python quilt

build-dep: debian/control
	if which aptitude >/dev/null; then sudo -E mk-build-deps -ir -t 'aptitude -R'; \
	else sudo -E mk-build-deps -ir; fi

get-orig-source:
	debian/rules ../sagemath_$(DEB_VERSION_UPSTREAM).orig.tar.xz

APT_PIN_PRIORITY = 100
install-apt-sources:
	sudo apt-get install -y apt-transport-https
	for d in deb deb-src; do \
	  echo "$$d https://debian-science.alioth.debian.org/apt sid-sage/"; done \
	  | sudo tee /etc/apt/sources.list.d/deb-sci-sage.list
	sudo apt-key add $(CURDIR)/debian/deb-sci-sage.asc
	printf "Package: *\nPin: release n=sid-sage\nPin-Priority: $(APT_PIN_PRIORITY)\n" \
	  | sudo tee /etc/apt/preferences.d/deb-sci-sage
	sudo apt-get update

ifneq (1,$(TRY_AVOID_REBUILD))
REBUILD_RELEASE = ../sagemath_$(DEB_VERSION).dsc
endif

SBUILD = sbuild --build-failed-commands '%SBUILD_SHELL'
SBUILD_REPO_EXPERIMENTAL = --extra-repository="deb http://httpredir.debian.org/debian experimental main"
# Sometimes this is necessary, if the mirrors have different versions for different architectures
ifeq (1,$(SBUILD_USE_INCOMING))
SBUILD += --extra-repository="deb http://incoming.debian.org/debian-buildd buildd-unstable main"
SBUILD_REPO_EXPERIMENTAL += --extra-repository="deb http://incoming.debian.org/debian-buildd buildd-experimental main"
endif

SBUILD_REPO_DEBSCISAGE = \
	  --chroot-setup-commands='apt-get install -y ca-certificates' \
	  --extra-repository='deb https://debian-science.alioth.debian.org/apt sid-sage/' \
	  --extra-repository-key=$(CURDIR)/debian/deb-sci-sage.asc \

# If the sbuild rules fail for you, try these workarounds:
#
# 0 https://wiki.debian.org/sbuild#Using_aliases
#   if schroot complains about non-existent chroot
#
# 1 bottom of https://wiki.debian.org/DebianScience/Sage
#   for misc other fixes
#
# 2 echo "/var/cache/apt/archives /var/cache/apt/archives none rw,bind 0 0" >> /etc/schroot/sbuild/fstab
#   If you get download errors and sbuild isn't smart enough to ask apt to retry
#

release: $(REBUILD_RELEASE)
	! $(is_prerelease)
	cd .. && $(SBUILD) \
	  $(SBUILD_EXTRA_FLAGS) \
	  "sagemath_$(DEB_VERSION).dsc"

release-experimental: $(REBUILD_RELEASE)
	! $(is_prerelease)
	cd .. && $(SBUILD) -d experimental \
	  $(SBUILD_REPO_EXPERIMENTAL) --build-dep-resolver=aspcud \
	  $(SBUILD_EXTRA_FLAGS) \
	  "sagemath_$(DEB_VERSION).dsc"

prerelease-unstable: $(REBUILD_RELEASE)
	$(is_prerelease)
	cd .. && $(SBUILD) -d UNRELEASED \
	  $(SBUILD_REPO_DEBSCISAGE)  --build-dep-resolver=aspcud \
	  $(SBUILD_EXTRA_FLAGS) \
	  "sagemath_$(DEB_VERSION).dsc"

prerelease: $(REBUILD_RELEASE)
	$(is_prerelease)
	cd .. && $(SBUILD) -d UNRELEASED \
	  $(SBUILD_REPO_EXPERIMENTAL) --build-dep-resolver=aspcud \
	  $(SBUILD_REPO_DEBSCISAGE) \
	  $(SBUILD_EXTRA_FLAGS) \
	  "sagemath_$(DEB_VERSION).dsc"
