#!/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_RERUN = $(shell expr 2 \* $(MAX_TEST_FAILURES))

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

# Some buildds are a bit slow, see https://buildd.debian.org/status/logs.php?pkg=gcc-7 for a fuller list
ifneq (,$(filter $(DEB_BUILD_ARCH),armel armhf mips mipsel mips64el))
export SAGE_TIMEOUT_LONG = 7200
else
export SAGE_TIMEOUT_LONG = 3600
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_ARCH = --optional=sage,python3,memlimit --long
SAGE_TEST_FLAGS_INDEP = --optional=sage,python3,memlimit,dochtml --long
else
SAGE_TEST_FLAGS_ARCH = --optional=sage,python3,memlimit
SAGE_TEST_FLAGS_INDEP = --optional=sage,python3,memlimit,dochtml
endif

export SAGE_LOCAL = $(CURDIR)/debian/build/usr
export PYTHONPATH = $(CURDIR)/debian/build/usr/lib/python3/dist-packages
export DOT_SAGE = $(CURDIR)/debian/test
export SAGE_SCRIPTS_DIR = $(CURDIR)/sage/src/bin
export SAGE_PYTHON3=yes
export SAGE_PYTHON_VERSION=3

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

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

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

binary build clean install: debian/control

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

../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 sage && python3 ../debian/pruner.py --sageroot="$(CURDIR)/sage"
	touch prune

run_build = \
	MAKE='make -j$(NUMJOBS)' PREREQ_OPTIONS="--prefix=$(SAGE_LOCAL)" LDFLAGS="-Wl,-rpath,/usr/lib/$(DEB_HOST_MULTIARCH)/gap" \
	  $(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
	cd debian/build/usr/share/doc/sagemath/html && \
	for statdir in */*/_static; do ln -s /usr/share/javascript/mathjax "$$statdir"; done && \
	for statdir in */_static; do ln -s /usr/share/javascript/mathjax "$$statdir"; done
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/
# Install sage-python23 which is required at runtime but located in build/bin.
	cp -f sage/build/bin/sage-python23  debian/tmp/usr/bin/
# Don't install sage-env-config.
	rm -f debian/tmp/usr/bin/sage-env-config
# Install (renamed) icons.
	mkdir -p debian/tmp/usr/share/icons/hicolor/64x64/apps
	mkdir -p debian/tmp/usr/share/icons/hicolor/scalable/apps
	cp -f sage/src/ext/notebook-ipython/logo-64x64.png debian/tmp/usr/share/icons/hicolor/64x64/apps/sagemath.png
	cp -f sage/src/ext/notebook-ipython/logo.svg debian/tmp/usr/share/icons/hicolor/scalable/apps/sagemath.svg
# 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
	rm -f sage/config.status
endif

override_dh_install-indep:
	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.
	rdfind -outputname /dev/null -makehardlinks true debian/sagemath-doc; \

override_dh_python3-arch:
	dh_python3
	dh_numpy3 --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 -rf $(DOT_SAGE)
	$(call preserve_docs,dh_clean,debian/tmp)
	$(call preserve_docs,rm -rf debian/build/*,debian/build)

override_dh_strip_nondeterminism:
	dh_strip_nondeterminism -X.png # On png files it sometimes fails claiming it's not a png file.

###############################################################################
# 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) --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 MAX_TEST_FAILURES=$(3)
run_tests_with_retry = \
	$(run_tests); cd "$(CURDIR)" && \
	if $(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)))
	$(call run_tests_with_retry,arch,$(SAGE_TEST_FLAGS_ARCH) src/sage,350)
endif

override_dh_auto_test-indep:
ifeq (,$(filter nodoc,$(DEB_BUILD_OPTIONS)))
ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
	$(call run_tests_with_retry,indep,$(SAGE_TEST_FLAGS_INDEP) src/doc src/sage/misc/sagedoc.py,25)
	# 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)
	@echo export SAGE_PYTHON3=$(SAGE_PYTHON3)
	@echo export SAGE_PYTHON_VERSION=$(SAGE_PYTHON_VERSION)

# 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

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

# 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)
	cd .. && $(SBUILD) \
	  $(SBUILD_EXTRA_FLAGS) \
	  "sagemath_$(DEB_VERSION).dsc"

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