#!/usr/bin/make -f

DPKG_EXPORT_BUILDFLAGS = 1
include /usr/share/dpkg/default.mk
include /usr/share/dpkg/architecture.mk

OS_ID := $(shell grep -E "^ID" /etc/os-release | sed "s/^ID=//")

DEB_SOURCE_PACKAGE := $(strip $(shell egrep '^Source: ' debian/control | cut -f 2 -d ':'))
DEB_VERSION := $(shell dpkg-parsechangelog | egrep '^Version:' | cut -f 2 -d ' ')
DEB_EPOCH := $(shell echo $(DEB_VERSION) | cut -d: -f1)
DEB_NOEPOCH_VERSION := $(shell echo $(DEB_VERSION) | cut -d: -f2-)
DEB_UPSTREAM_VERSION := $(shell echo $(DEB_NOEPOCH_VERSION) | sed 's/-[^-]*$$//')
DEB_STRIPPED_UPSTREAM_VERSION = $(shell echo $(DEB_UPSTREAM_VERSION) | sed -e 's/\+dfsg.*$$//p')

DEBIAN_VERSION_ID := $(OS_ID)_$(DEB_VERSION)

DOLFINX_RELEASE_VERSION=$(DEB_STRIPPED_UPSTREAM_VERSION)
DOLFINX_MAJOR_VERSION=$(shell echo $(DOLFINX_RELEASE_VERSION) | sed "s/^\([^.]*\)\..*$$/\1/")
DOLFINX_MINOR_VERSION=$(shell echo $(DOLFINX_RELEASE_VERSION) | sed "s/^\([^.]*\)\.\([^.]*\)\..*$$/\2/")
DOLFINX_VERSION=$(DOLFINX_MAJOR_VERSION).$(DOLFINX_MINOR_VERSION)
DOLFINX_NEXT_VERSION=$(DEB_EPOCH):$(DOLFINX_MAJOR_VERSION).$(shell echo $$(( $(DOLFINX_MINOR_VERSION) + 1 )) )

# dolfinx depends on the nanobind version it was built against,
# if nanobind.h will be used in C++ code fragments in python scripts.
# But nanobind follows semantic versioning, so should have backwards compatibility
# with minor and patch version updates.
# Extract nanobind version from nanobind-dev
NANOBIND_DEB_VERSION=$(shell dpkg -s nanobind-dev | awk '/Version:/ {print $$2}')
# extract the current nanobind version X.Y.Z (drop epoch and debian package version)
NANOBIND_UPSTREAM_VERSION=$(shell echo $(NANOBIND_DEB_VERSION) | sed "s/^.[^:]*://; s/-[^-]*$$//")
NANOBIND_X_VERSION=$(shell echo $(NANOBIND_UPSTREAM_VERSION) | sed "s/^\([^.]*\).*/\1/")
NANOBIND_X_Y_VERSION=$(shell echo $(NANOBIND_UPSTREAM_VERSION) | sed "s/^\(.*\)\.\([^.]*\)$$/\1/")
NANOBIND_VERSION=$(NANOBIND_X_Y_VERSION)
NANOBIND_NEXT_VERSION=$(shell echo $$(( $(NANOBIND_X_VERSION) + 1 )) )

# Allow test programs that use OpenMPI to run
export PRTE_MCA_plm_ssh_agent=/bin/false
export OMPI_MCA_btl_base_warn_component_unused=0

include /usr/share/mpi-default-dev/debian_defaults
ENABLE_MPI=ON
ifeq ($(findstring $(DEB_BUILD_ARCH),$(OPENMPI_ARCHITECTURES)),)
MPIEXEC_PARAMS=
else
MPIEXEC_PARAMS=--oversubscribe
endif

DOLFINX_HOME = $(CURDIR)/$(DEB_SRCDIR)
USCAN_DESTDIR := $(CURDIR)
PY3VER_DEFAULT := $(shell py3versions -dv)

BUILDDIR_REAL = $(CURDIR)/obj-$(DEB_HOST_GNU_TYPE)-real
BUILDDIR_COMPLEX = $(CURDIR)/obj-$(DEB_HOST_GNU_TYPE)-complex

NPROC := $(shell nproc)
PARALLEL = $(subst parallel=,,$(filter parallel=%,$(DEB_BUILD_OPTIONS)))
N_CPU    = $(or $(PARALLEL),$(NPROC),1)
# MPI tests are set up to run on 3 processes. Spread them leanly over the available processors (don't have more than 2 tests using one processor).
# e.g. run 1 MPI test at a time over 1-3 processors, or 2 tests at a time over 4-6 processors, or 3 tests over 7-9 processors, etc.
N_MPI := 3
N_MPI_TESTS = $(shell echo $$(( ($(N_CPU)+$(N_MPI)-1)/$(N_MPI) )) )

ifeq (nocheck,$(findstring nocheck,$(DEB_BUILD_OPTIONS)))
  RUNTEST=no
endif

# some arches just aren't keeping up at all, and fail non-MPI tests
ARCH_SKIP_ALL_TESTS_LIST = mips64el hurd-i386 m68k sh4

# monitor slow python tests
export PYBUILD_TEST_ARGS = --durations=20

empty :=
space := $(empty)$(empty)

# GJK (test_cube_distance in test_gjk) fails to converge on many arches
SKIP_GJK_TEST_ARCH_LIST = armel armhf i386 mips64el sparc64
ifneq (,$(findstring $(space)$(DEB_HOST_ARCH)$(space), $(space)$(SKIP_GJK_TEST_ARCH_LIST)$(space)))
  SKIP_TEST_LIST += test_cube_distance
endif

# some tests do not converge on some arches
# test_nonlinear_pde, test_petsc_solver_wrappers.py TestPETScSolverWrappers.test_compare_solvers
# "Newton solver did not converge because maximum number of iterations reached"
SKIP_NONLINEAR_PDE_TEST_ARCH_LIST = mips64el ppc64
ifneq (,$(findstring $(space)$(DEB_HOST_ARCH)$(space), $(space)$(SKIP_NONLINEAR_PDE_TEST_ARCH_LIST)$(space)))
    SKIP_TEST_LIST += test_nonlinear_pde test_compare_solvers
endif

# skip slowest tests on riscv64, which is most of them
ifeq ($(DEB_HOST_ARCH),riscv64)
    SKIP_TEST_LIST += test_symmetry test_curl_curl_eigenvalue test_higher_order_function test_eigen_assembly \
	test_assembler test_fem_pipeline test_dof_permuting test_dofmap test_element_integrals test_xdmf_function \
	test_assemble_submesh test_interpolation test_petsc_discrete_operators test_mesh test_higher_order_mesh
endif

export PYBUILD_TEST_ARGS += $(shell \
        SKIP_TESTS=""; \
        list_initialised=0; \
        for t in $(SKIP_TEST_LIST); do \
            if [ $${list_initialised} = 0 ]; then \
                SKIP_TESTS=$$t; \
                list_initialised=1; \
            else \
                SKIP_TESTS="$${SKIP_TESTS} or $$t"; \
            fi; \
        done; \
        if [ "x$${SKIP_TESTS}" != "x" ]; then \
            SKIP_TESTS="not ( $${SKIP_TESTS} )"; \
        fi; \
        echo "-k \"$${SKIP_TESTS}\"")

# extract PETSc version from petsc-dev
PETSC_DEB_VERSION=$(shell dpkg -s petsc-dev | awk '/Version:/ {print $2}')
# extract the current PETSc version
PETSC_UPSTREAM_VERSION=$(shell pkg-config --modversion PETSc)
# "Major" version is the first number in the upstream version (major.minor.release)
PETSC_MAJOR_VERSION=$(shell echo $(PETSC_UPSTREAM_VERSION) | sed "s/^\([^.]*\)\..*$$/\1/")
# "Minor" version is the second number in the upstream version (major.minor.release)
PETSC_MINOR_VERSION=$(shell echo $(PETSC_UPSTREAM_VERSION) | sed "s/^\([^.]*\)\.\([^.]*\)\..*$$/\2/")
PETSC_VERSION=$(PETSC_MAJOR_VERSION).$(PETSC_MINOR_VERSION)
PETSC_VERSION_NEXT=$(shell echo $(PETSC_MAJOR_VERSION).$$(($(PETSC_MINOR_VERSION)+1)))

SLEPC_UPSTREAM_VERSION=$(shell pkg-config --modversion SLEPc)
# SLEPc version must match PETSc
SLEPC_VERSION=$(PETSC_VERSION)
SLEPC_VERSION_NEXT=$(PETSC_VERSION_NEXT)

PETSC_DIR_BASE=/usr/lib/petscdir/petsc$(PETSC_VERSION)/$(DEB_HOST_MULTIARCH)
SLEPC_DIR_BASE=/usr/lib/slepcdir/slepc$(SLEPC_VERSION)/$(DEB_HOST_MULTIARCH)
export PETSC_DIR_REAL=$(PETSC_DIR_BASE)-real
SLEPC_DIR_REAL=$(SLEPC_DIR_BASE)-real
export PETSC_DIR_COMPLEX=$(PETSC_DIR_BASE)-complex
SLEPC_DIR_COMPLEX=$(SLEPC_DIR_BASE)-complex

DEB_CXXFLAGS := $(shell dpkg-buildflags --get CXXFLAGS)

ifeq ($(DEB_HOST_ARCH),mips64el)
  DEB_CXXFLAGS += -Wno-error=maybe-uninitialized
endif

CMAKE_OPTS := \
	-D CMAKE_BUILD_TYPE:STRING=RelWithDebInfo \
	-D BUILD_SHARED_LIBS:BOOL=ON \
	-D CMAKE_SKIP_RPATH:BOOL=ON \
	-D CMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=OFF \
	-D DOLFINX_ENABLE_TRILINOS:BOOL=OFF \
	-D DOLFINX_ENABLE_HDF5:BOOL=ON \
	-D HDF5_C_COMPILER_EXECUTABLE:FILEPATH=/usr/bin/h5pcc \
	-D DOLFINX_ENABLE_PARMETIS:BOOL=OFF \
	-D DOLFINX_ENABLE_SCOTCH:BOOL=ON \
	-D DOLFINX_ENABLE_DOCS:BOOL=OFF \
	-D DOLFINX_ENABLE_MPI:BOOL=$(ENABLE_MPI) \
	-D MPIEXEC_PARAMS:STRING="$(MPIEXEC_PARAMS)" \
	-D CMAKE_CXX_FLAGS:STRING="-fpermissive" \
	-D DOLFINX_EXTRA_CXX_FLAGS:STRING="$(DEB_CXXFLAGS)" \
	-D DEBIAN_VERSION_ID=$(DEBIAN_VERSION_ID) \
	$(XSIMD_CONFIG)

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

override_dh_compress:
	dh_compress -X.py -X.cpp -X.h -X.pdf -X.ufl

override_dh_auto_clean:
	dh_auto_clean --builddir=$(BUILDDIR_REAL)
	dh_auto_clean --builddir=$(BUILDDIR_COMPLEX)
	rm -rf python/build-real python/build-complex
	rm -rf debian/tmp-real debian/tmp-complex
	rm -rf python/fenics_dolfinx.egg-info
	rm -rf python/doc/build python/doc/source/generated
	rm -rf cpp/doc/latex cpp/doc/html cpp/doc/xml
	rm -rf $(CURDIR)/python/doc/source/demos

override_dh_auto_configure:
	PETSC_DIR=$(PETSC_DIR_REAL) SLEPC_DIR=$(SLEPC_DIR_REAL) dh_auto_configure --builddir=$(BUILDDIR_REAL) -D$(CURDIR)/cpp -- $(CMAKE_OPTS)
	PETSC_DIR=$(PETSC_DIR_COMPLEX) SLEPC_DIR=$(SLEPC_DIR_COMPLEX) dh_auto_configure --builddir=$(BUILDDIR_COMPLEX) -D$(CURDIR)/cpp -- $(CMAKE_OPTS)

override_dh_auto_build:
	PETSC_DIR=$(PETSC_DIR_REAL) SLEPC_DIR=$(SLEPC_DIR_REAL) dh_auto_build --builddir=$(BUILDDIR_REAL)
	PETSC_DIR=$(PETSC_DIR_COMPLEX) SLEPC_DIR=$(SLEPC_DIR_COMPLEX) dh_auto_build --builddir=$(BUILDDIR_COMPLEX)

override_dh_auto_test-arch:
	echo "Tests must be run after installation"

override_dh_auto_test-indep:

debian/tmp-real:
	dh_auto_install --builddir=$(BUILDDIR_REAL) --destdir=debian/tmp-real
	cp COPYING.LESSER python
	for build in real; do \
		PATH=$(CURDIR)/debian/tmp-$${build}/usr/bin:$$PATH \
		PETSC_DIR=$(PETSC_DIR_BASE)-$${build} SLEPC_DIR=$(SLEPC_DIR_BASE)-$${build} \
		CXXFLAGS="$(DEB_CXXFLAGS) -isystem $(CURDIR)/debian/tmp-$${build}/usr/include" \
		VERBOSE=1 \
		ADIOS2_ALWAYS_USE_MPI=1 \
		PUSIMP_EXPECT_DOLFINX_IN_CWD=1 \
		pybuild --dir=$(CURDIR)/python --dest-dir=$(CURDIR)/debian/tmp-$${build} --system=pyproject --name=dolfinx-$${build} --test-pytest; \
	done

override_dh_auto_install-indep: debian/tmp-real
	dh_auto_install -i --builddir=$(BUILDDIR_COMPLEX) --destdir=debian/tmp-complex
	(cd cpp/doc; doxygen)
	(cd python/doc; \
	  mkdir -p build/pkgconfig; \
	  cp $(CURDIR)/debian/tmp-real/usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig/dolfinx_real.pc build/pkgconfig; \
	  sed "s|prefix=/usr|prefix=$(CURDIR)/debian/tmp-real/usr|" -i build/pkgconfig/dolfinx_real.pc; \
	  PYTHONPATH=$(CURDIR)/debian/tmp-real/usr/lib/python$(PY3VER_DEFAULT)/dist-packages \
	    PKG_CONFIG_PATH=`pwd`/build/pkgconfig:$${PKG_CONFIG_PATH} \
	    LD_LIBRARY_PATH=$(CURDIR)/debian/tmp-real/usr/lib/$(DEB_HOST_MULTIARCH)/:$${LD_LIBRARY_PATH} \
	    XDG_CACHE_HOME=`pwd`/build \
	    RDMAV_FORK_SAFE=1 \
	    MPLBACKEND=agg \
	    python3 -m sphinx -W -b html source/ .pybuild/html/)
	dh_numpy3 -i

override_dh_auto_install-arch: debian/tmp-real
	dh_auto_install -a --builddir=$(BUILDDIR_COMPLEX) --destdir=debian/tmp-complex
	cp COPYING.LESSER python
	dh_numpy3 -a
	for build in complex; do \
		PATH=$(CURDIR)/debian/tmp-$${build}/usr/bin:$$PATH \
		PETSC_DIR=$(PETSC_DIR_BASE)-$${build} SLEPC_DIR=$(SLEPC_DIR_BASE)-$${build} \
		CXXFLAGS="$(DEB_CXXFLAGS) -isystem $(CURDIR)/debian/tmp-$${build}/usr/include" \
		VERBOSE=1 \
		PUSIMP_EXPECT_DOLFINX_IN_CWD=1 \
		pybuild --dir=$(CURDIR)/python --dest-dir=$(CURDIR)/debian/tmp-$${build} --system=pyproject --name=dolfinx-$${build} --test-pytest; \
	done
	chrpath -d $(CURDIR)/debian/tmp*/usr/lib/python*/dist-packages/dolfinx/*.so
	sed -i "s/-D_FORTIFY_SOURCE=2//g" $(CURDIR)/debian/tmp*/usr/lib/$(DEB_BUILD_MULTIARCH)/pkgconfig/dolfinx*.pc
	sed -i "s|-DNDEBUG||g" $(CURDIR)/debian/tmp*/usr/lib/$(DEB_BUILD_MULTIARCH)/pkgconfig/dolfinx*.pc

	if [ "x$(RUNTEST)" != "xno" ]; then \
	  case " $(ARCH_SKIP_ALL_TESTS_LIST) " in \
	    *\ $(DEB_HOST_ARCH)\ *) echo "ALL tests have been disabled on $(DEB_HOST_ARCH)";; \
	    *) set -e; \
	       export CTEST_OUTPUT_ON_FAILURE=1; \
	       for build in real complex; do \
	         echo "== testing $$build number build =="; \
	         builddir=$(CURDIR)/obj-$(DEB_HOST_GNU_TYPE)-$${build}; \
	         for mytest in test demo; do \
	           echo "running tests from $$mytest for $$build build"; \
	           testdir=$${builddir}/run_$${mytest}; \
	           mkdir -p $$testdir; \
	           cd $$testdir; \
	           if [ "x$(SKIP_TEST_LIST)" != "x" ]; then echo "set(CTEST_CUSTOM_TESTS_IGNORE $(SKIP_TEST_LIST) )" >> CTestCustom.cmake; fi; \
	           PETSC_DIR=$(PETSC_DIR_BASE)-$${build} SLEPC_DIR=$(SLEPC_DIR_BASE)-$${build}  cmake  -DCMAKE_MODULE_PATH=$(CURDIR)/debian/tmp-$${build}/usr/lib/$(DEB_BUILD_MULTIARCH)/cmake/dolfinx \
	                                                                                               -DDOLFINX_DIR=$(CURDIR)/debian/tmp-$${build}/usr/lib/$(DEB_BUILD_MULTIARCH)/cmake/dolfinx \
	                                                                                               -DMPIEXEC_PARAMS:STRING="$(MPIEXEC_PARAMS)" \
	                                                                                               $(CURDIR)/cpp/$${mytest}; \
	           make all  VERBOSE=1; \
	           OMPI_ALLOW_RUN_AS_ROOT=1 OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1 make test VERBOSE=1; \
	         done; \
	         cd $(CURDIR); \
	       done; \
	  esac; \
	fi

override_dh_install-arch:
	dh_install -a
	sed "s/set(CMAKE_IMPORT_FILE_VERSION 1)/set(CMAKE_IMPORT_FILE_VERSION 1)\n\nif(PETSC_SCALAR_COMPLEX)\n  set(LIB_NAME_EXT \"_complex\")\nelse()\n  set(LIB_NAME_EXT \"_real\")\nendif()/; \
          s/libdolfinx_real.so/libdolfinx\$${LIB_NAME_EXT}.so/g" \
          -i debian/libdolfinx-dev/usr/lib/$(DEB_HOST_MULTIARCH)/cmake/dolfinx/DOLFINXTargets-relwithdebinfo.cmake

override_dh_python3-arch:
	dh_python3 -a
	dh_numpy3 -a
	mkdir -p debian/python3-dolfinx-real/$(PETSC_DIR_REAL)/lib
	mv debian/python3-dolfinx-real/usr/lib/python3  debian/python3-dolfinx-real/$(PETSC_DIR_REAL)/lib
	rm -rf debian/python3-dolfinx-real/$(PETSC_DIR_REAL)/lib/python3/dist-packages/dolfinx_utils
	mkdir -p debian/python3-dolfinx-complex/$(PETSC_DIR_COMPLEX)/lib
	mv debian/python3-dolfinx-complex/usr/lib/python3  debian/python3-dolfinx-complex/$(PETSC_DIR_COMPLEX)/lib
	rm -rf debian/python3-dolfinx-complex/$(PETSC_DIR_COMPLEX)/lib/python3/dist-packages/dolfinx_utils

# set petsc:Depends to something like "libpetsc-real3.8-dev, libslepc-real3.8-dev, python-petsc4py (>= 3.8), python-petsc4py (<< 3.9), python-slepc4py (>= 3.8), python-slepc4py (<< 3.9)"
PETSC_DEV_DEPENDS="libpetsc-real$(PETSC_VERSION)-dev, libslepc-real$(SLEPC_VERSION)-dev"
PETSC_COMPLEX_DEV_DEPENDS="libpetsc-complex$(PETSC_VERSION)-dev, libslepc-complex$(SLEPC_VERSION)-dev"
# slepc4py version must match petsc4py (using the PETSc minor version, not the patch release)
PETSC4PY_DEPENDS_PY3=python3-petsc4py, python3-slepc4py, python3-petsc4py-real (>= $(PETSC_VERSION)), python3-petsc4py-real (<< $(PETSC_VERSION_NEXT)), python3-slepc4py-real (>= $(SLEPC_VERSION)), python3-slepc4py-real (<< $(SLEPC_VERSION_NEXT))
PETSC4PY_COMPLEX_DEPENDS_PY3=python3-petsc4py, python3-slepc4py, python3-petsc4py-complex (>= $(PETSC_VERSION)), python3-petsc4py-complex (<< $(PETSC_VERSION_NEXT)), python3-slepc4py-complex (>= $(SLEPC_VERSION)), python3-slepc4py-complex (<< $(SLEPC_VERSION_NEXT))
override_dh_gencontrol:
	echo "python3-petsc4py-real:Depends=$(PETSC4PY_DEPENDS_PY3)" >> debian/python3-dolfinx-real.substvars
	echo "python3-petsc4py-complex:Depends=$(PETSC4PY_COMPLEX_DEPENDS_PY3)" >> debian/python3-dolfinx-complex.substvars
	echo "python-petsc4py-alt:Depends=$(PETSC4PY_DEPENDS_PY3)" >> debian/libdolfinx-dev.substvars
	dh_gencontrol -- -Vpetsc:Depends=$(PETSC_DEV_DEPENDS) -Vpetsc-complex:Depends=$(PETSC_COMPLEX_DEV_DEPENDS) -Vdolfinx:Next-Upstream-Version=$(DOLFINX_NEXT_VERSION)~ \
	    -Vnanobind:Upstream-Version=$(NANOBIND_VERSION) -Vnanobind:Next-Upstream-Version=$(NANOBIND_NEXT_VERSION)

# dbgsym-migration was introduced for dolfin 2017.2 (don't update the version to a later version)
override_dh_strip:
	dh_strip --package=libdolfinx-real$(DOLFINX_VERSION) -Xcomplex -Xpython
	dh_strip --package=libdolfinx-complex$(DOLFINX_VERSION) -Xreal -Xpython
	dh_strip --package=python3-dolfinx-real

# python module so files are already stripped by pybuild
override_dh_dwz:
	dh_dwz -Xcpp.cpython

# https://stackoverflow.com/a/18793112/353337
override_dh_shlibdeps:
	dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info


.PHONY: get-orig-source override_dh_strip
