#! /usr/bin/make -f

DEB_BUILD_MAINT_OPTIONS = hardening=+all
# b2 compiled the libraries as variant=release, which implies -DNDEBUG, while
# the CMake build type is None.  Keep the generated code the same.
DEB_CPPFLAGS_MAINT_APPEND = -DNDEBUG
DEB_CXXFLAGS_MAINT_APPEND = -Wno-unused-local-typedefs

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

# The CMake build configures and builds several separate trees (shared,
# static, and one more of each per extra Python interpreter); with debhelper's
# default VERBOSE=1 that prints every compiler invocation and floods the
# Salsa CI log. "terse" makes dh_auto_configure/dh_auto_build quiet instead.
export DEB_BUILD_OPTIONS += terse

# Boost libraries for which we want separate packages
boost_libs := atomic charconv chrono cobalt container context contract	\
              coroutine date-time exception fiber filesystem graph	\
              graph-parallel iostreams json locale log math mpi		\
              mpi-python nowide process program-options python random	\
              regex serialization stacktrace test thread timer		\
              type-erasure url wave

# these are special cases, where /usr/lib name differs from Boost library name
boost_lib_cobalt := cobalt cobalt_io
boost_lib_log := log log_setup
boost_lib_math := math_c99 math_c99f math_tr1 math_tr1f
boost_lib_math_long_double := math_c99l math_tr1l
boost_lib_serialization := serialization wserialization
# stacktrace_dump is a private helper library the other stacktrace backends
# link against, and their CMake configuration files require it.
ifeq ($(DEB_BUILD_ARCH), m68k)
# See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=962072
boost_lib_stacktrace := stacktrace_dump stacktrace_noop stacktrace_addr2line stacktrace_basic
else
boost_lib_stacktrace := stacktrace_dump stacktrace_noop stacktrace_addr2line stacktrace_backtrace stacktrace_basic
endif
boost_lib_test := prg_exec_monitor test_exec_monitor unit_test_framework

pyversions := $(shell py3versions -rv)
pydefault := $(shell py3versions -dv)
# Boost.Python is built once per interpreter; the default one comes out of the
# main build, the rest get a CMake pass of their own.
pyextra := $(filter-out $(pydefault),$(pyversions))
pyverids := $(subst .,,$(pyversions))

# These are special cases for suffixes.
boost_suffixes_python := $(pyverids)
boost_suffixes_numpy := $(pyverids)
boost_suffixes_mpi-python := $(pyverids)

version_full := $(shell dpkg-parsechangelog | grep Version | cut -d' ' -f2)
version_upstream := $(shell echo $(version_full) | cut -d'-' -f1 | cut -d '+' -f1 | cut -d'~' -f1)
version_major := $(shell echo $(version_upstream) | cut -d'.' -f1,2)

PKGVERSION = $(version_major)
SOVERSION = $(version_upstream)

# Function to map Boost component name to set of shared library names
# Input: Boost component name
# Return: shared library names for the given Boost library
boost_lib = $(if $(boost_lib_$(1)), $(boost_lib_$(1)), $(1))

# Function to map Boost component name to set of suffixes for the library
# Input: Boost component name
# Return: suffixes for the given Boost component
boost_suffixes = $(if $(boost_suffixes_$(1)), $(boost_suffixes_$(1)),"")

# Helpers to make basic and decorated library names
# Input: library, suffix
# Return: base library filename, CMake package directory (without variant)
mk_base_name = usr/lib/$(DEB_HOST_MULTIARCH)/libboost_$(subst -,_,$(1))$(2)
mk_cmake_name = usr/lib/$(DEB_HOST_MULTIARCH)/cmake/boost_$(subst -,_,$(1))$(2)-$(SOVERSION)

# Input: component
# Return: package name for shared library or development
mk_pkg_lib = libboost-$(1)$(SOVERSION)
mk_pkg_dev = libboost-$(1)$(PKGVERSION)-dev
# Helpers to generate debhelper input filenames.
# Input: component
# Return: prefix to debhelper filenames
mk_deb_lib = debian/$(call mk_pkg_lib,$(1))
mk_deb_dev = debian/$(call mk_pkg_dev,$(1))

# Helpers that update debhelper .install files
# Input: component, library, suffix
# Output: none
mk_so_files = $(shell echo debian/tmp/$(call mk_base_name,$(2),$(3)).so.$(SOVERSION) >> $(call mk_deb_lib,$(1)).install)
mk_ln_files = $(shell echo debian/tmp/$(call mk_base_name,$(2),$(3)).so >> $(call mk_deb_dev,$(1)).install)
mk_a_files = $(shell echo debian/tmp/$(call mk_base_name,$(2),$(3)).a >> $(call mk_deb_dev,$(1)).install)
mk_cmake_shared_files = $(shell echo debian/tmp/$(call mk_cmake_name,$(2),$(3))-shared >> $(call mk_deb_dev,$(1)).install)
mk_cmake_static_files = $(shell echo debian/tmp/$(call mk_cmake_name,$(2),$(3))-static >> $(call mk_deb_dev,$(1)).install)

# Specify the type of files/links to install.
# Special cases first, then general rule
boost_filetypes_exception = a cmake_static
boost_filetypes_test_exec_monitor = a cmake_static
boost_filetypes = $(if $(boost_filetypes_$(1)), $(boost_filetypes_$(1)),a so ln cmake_shared cmake_static)

# Function that updates debhelper files for a given library
# Input: component, library, suffix
# Output: none
mk_files = $(foreach fn,$(call boost_filetypes,$(2)),$(call mk_$(fn)_files,$(1),$(2),$(3)))

# Components that additionally install an unversioned CMake package acting as
# an alias for the interpreter specific one.
# Input: component
boost_libs_python := python numpy mpi-python
mk_alias_files = $(shell echo debian/tmp/$(call mk_cmake_name,$(1),) >> $(call mk_deb_dev,$(1)).install)

# helpers to make and install lintian override files

# Input: package, override
add_override = echo $(1): $(2) >> debian/$(1).lintian-overrides;

# Input: override
add_dev_override = $(call add_override,libboost$(PKGVERSION)-dev,$(1))

# Input: component, lintian-warning
add_lib_override = $(call add_override,$(call mk_pkg_lib,$(1)),$(2))

# Input: package-name-base, versioned-package-name
cp_debhelper = set -e ; for s in doc-base examples postinst prerm README.Debian; do \
	if test -f debian/$(1).$$s; then cp -f debian/$(1).$$s debian/$(2).$$s; fi; done

# Components whose shared library names never match the name of the package
# shipping them, so lintian's package-name-doesnt-match-sonames has to be
# overridden.  For every other component the package name matches at least one
# of its sonames and the override would be unused.
boost_libs_soname_mismatch := math mpi-python numpy python stacktrace test

# Components whose shared libraries genuinely have no dependencies of their own
# apart from the C library, which lintian mistakes for a missing linker flag.
boost_libs_no_prerequisites := date-time stacktrace

# Function that updates debhelper files for all libraries shipped.
mk_debhelper_files = \
	$(call add_dev_override,extra-license-file) \
	$(foreach l, $(boost_libs_no_prerequisites), \
		$(call add_lib_override,$(l),shared-library-lacks-prerequisites)) \
	$(foreach l, $(boost_libs), \
		echo "making debhelper files for $(l)..."; \
		$(if $(filter $(l),$(boost_libs_soname_mismatch)), \
			$(call add_lib_override,$(l),package-name-doesnt-match-sonames)) \
		$(if $(filter $(l),$(boost_libs_python)),$(call mk_alias_files,$(l))) \
		$(foreach ll, $(call boost_lib,$(l)), \
			$(foreach suf, $(call boost_suffixes,$(l)), \
				$(call mk_files,$(l),$(ll),$(suf)) \
			) \
		) \
	)

BUILD_LONG_DOUBLE = yes
BUILD_NUMPY = yes

# Disable long double on some architectures
ifneq (,$(filter $(DEB_HOST_ARCH), alpha arm armel armhf arm64 ppc64el hppa mips mipsel sh4 powerpc ppc64))
BUILD_LONG_DOUBLE = no
CMAKE_ARCH_FLAGS += -DBOOST_MATH_HAS_LONG_DOUBLE=OFF
endif

ifeq ($(DEB_BUILD_ARCH), x32)
BUILD_NUMPY = no
endif

ifneq (,$(filter $(DEB_HOST_ARCH), alpha hppa m68k ppc64 sh4 x32))
CMAKE_ARCH_FLAGS += -DBOOST_CONTEXT_IMPLEMENTATION=ucontext
endif

# if we're building with long double, add the libraries to the list
ifeq ($(BUILD_LONG_DOUBLE), yes)
boost_lib_math += $(boost_lib_math_long_double)
endif

# if we're building with numpy, add the libraries to the list
ifeq ($(BUILD_NUMPY), yes)
boost_libs += numpy
endif

exampledir = debian/libboost$(PKGVERSION)-doc/usr/share/doc/libboost$(PKGVERSION)-doc/examples

# libboost_stacktrace_from_exception has never been shipped, see #1102107.
CMAKE_FLAGS = \
	-DBOOST_INSTALL_LAYOUT=system \
	-DBUILD_TESTING=OFF \
	-DBOOST_ENABLE_MPI=ON \
	-DBOOST_ENABLE_PYTHON=ON \
	-DBOOST_MATH_BUILD_WITH_LEGACY_FUNCTIONS=ON \
	-DBOOST_STACKTRACE_ENABLE_FROM_EXCEPTION=OFF \
	$(CMAKE_ARCH_FLAGS)

# The extra interpreters only need Boost.Python, Boost.NumPy and the Python
# bindings of Boost.MPI; everything else comes out of the main build.
CMAKE_PYTHON_FLAGS = -DBOOST_INCLUDE_LIBRARIES="python;mpi"

# Input: build directory suffix, extra CMake arguments
cmake_configure = dh_auto_configure --builddirectory=obj-$(1) -- $(CMAKE_FLAGS) $(2)

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

# Boost is built twice, once shared and once static, because a single CMake
# tree only ever produces one of the two variants.
override_dh_auto_configure-arch:
	$(call cmake_configure,shared,-DBUILD_SHARED_LIBS=ON -DPython_EXECUTABLE=/usr/bin/python$(pydefault))
	$(call cmake_configure,static,-DBUILD_SHARED_LIBS=OFF -DPython_EXECUTABLE=/usr/bin/python$(pydefault))
	set -e ; for v in $(pyextra); do \
		$(call cmake_configure,py$$v-shared,-DBUILD_SHARED_LIBS=ON $(CMAKE_PYTHON_FLAGS) -DPython_EXECUTABLE=/usr/bin/python$$v); \
		$(call cmake_configure,py$$v-static,-DBUILD_SHARED_LIBS=OFF $(CMAKE_PYTHON_FLAGS) -DPython_EXECUTABLE=/usr/bin/python$$v); \
	done

override_dh_auto_build-arch: make-debhelper
	dh_auto_build --builddirectory=obj-shared
	dh_auto_build --builddirectory=obj-static
	set -e ; for v in $(pyextra); do \
		dh_auto_build --builddirectory=obj-py$$v-shared; \
		dh_auto_build --builddirectory=obj-py$$v-static; \
	done

override_dh_auto_install-arch:
	# The main build goes last so that the unversioned Boost::python,
	# Boost::numpy and Boost::mpi_python CMake packages end up pointing at
	# the default interpreter.
	set -e ; for v in $(pyextra); do \
		dh_auto_install --builddirectory=obj-py$$v-static; \
		dh_auto_install --builddirectory=obj-py$$v-shared; \
	done
	dh_auto_install --builddirectory=obj-static
	dh_auto_install --builddirectory=obj-shared
	# Windows debugger visualisers and libraries Debian does not ship.
	rm -rf debian/tmp/usr/share/boost-$(SOVERSION)
	rm -f debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/libboost_fiber_numa.*
	rm -rf debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/cmake/boost_fiber_numa-*
	# b2 installed only the headers, CMake copies the whole include tree
	find debian/tmp/usr/include -type f \
		! -name '*.hpp' ! -name '*.ipp' ! -name '*.h' ! -name '*.inc' -delete
	# placeholders such as boost/headers/.gitkeep
	find debian/tmp -type f -empty -delete
	find debian/tmp -type d -empty -delete
	# b2 has no CMake build of its own, and bcp and quickbook are linked
	# against the static libraries, as they were with b2.
	cd tools/build && bison -y -d -o src/engine/jamgram.cpp src/engine/jamgram.y
	./bootstrap.sh || { cat bootstrap.log; exit 1; }
	SOURCE_DATE_EPOCH=$$(dpkg-parsechangelog -STimestamp) \
		help2man --name 'software build tool' --no-info ./b2 > b2.1
	cp b2.1 bjam.1
	set -e ; for t in bcp quickbook; do \
		dh_auto_configure --sourcedirectory=tools/$$t --builddirectory=obj-$$t -- \
			-DCMAKE_PREFIX_PATH=$(CURDIR)/debian/tmp/usr -DBoost_USE_STATIC_LIBS=ON; \
		dh_auto_build --builddirectory=obj-$$t; \
	done

# Nothing is compiled for the architecture independent packages: the
# documentation and the examples are shipped as they come in the tarball.
override_dh_auto_configure-indep:

override_dh_auto_build-indep:

override_dh_auto_install-indep:

override_dh_auto_test:

override_dh_auto_clean: clean-debhelper
	rm -rf obj-*
	rm -f tools/build/src/engine/jamgram.cpp tools/build/src/engine/jamgram.hpp
	rm -f tools/build/src/engine/b2 tools/build/src/engine/bjam
	rm -f b2 bjam b2.1 bjam.1 bootstrap.log project-config.jam project-config.jam.[0-9]*
	rm -vf debian/libboost$(version_major)-dev.examples debian/libboost$(version_major)-doc.README.Debian

override_dh_compress-indep:
	dh_compress -Xlibboost$(PKGVERSION)-doc/doc

override_dh_install-arch:
	# The CMake packages of the header only libraries have no shared or
	# static variant and belong to libboost-dev, except for the Python ones,
	# which are aliases claimed by their own -dev packages.
	find debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/cmake -mindepth 1 -maxdepth 1 \
		-name 'boost_*-$(SOVERSION)' \
		$(foreach c,$(boost_libs_python),! -name 'boost_$(subst -,_,$(c))-$(SOVERSION)') \
		-printf 'usr/lib/$(DEB_HOST_MULTIARCH)/cmake/%P\n' \
		>> debian/libboost$(PKGVERSION)-dev.install

	# Write substvars for Python packages
	echo 'boost:Provides=' $(foreach verid,$(pyverids),libboost-python$(SOVERSION)-py$(verid),) >> debian/libboost-python$(SOVERSION).substvars
	echo 'boost:Provides=' $(foreach verid,$(pyverids),libboost-mpi-python$(SOVERSION)-py$(verid),) >> debian/libboost-mpi-python$(SOVERSION).substvars
	echo 'boost:Provides=' $(foreach verid,$(pyverids),libboost-numpy$(SOVERSION)-py$(verid),) >> debian/libboost-numpy$(SOVERSION).substvars

	# package libboost$(PKGVERSION)-dev
	dh_install -plibboost$(PKGVERSION)-dev \
	   debian/tmp/usr/include/boost \
	   usr/include
	dh_install -plibboost$(PKGVERSION)-dev \
	   usr/lib/$(DEB_HOST_MULTIARCH)/cmake/Boost-$(SOVERSION)
	dh_install -plibboost$(PKGVERSION)-tools-dev \
	   b2 \
	   obj-bcp/bcp \
	   obj-quickbook/quickbook \
	   usr/bin
	dh_link -plibboost$(PKGVERSION)-tools-dev usr/bin/b2 usr/bin/bjam
	dh_installman -plibboost$(PKGVERSION)-tools-dev b2.1 bjam.1 debian/bcp.1 debian/inspect.1 debian/quickbook.1
	dh_install -plibboost$(PKGVERSION)-tools-dev tools/boostbook/xsl/* usr/share/boostbook/xsl
	dh_install -plibboost$(PKGVERSION)-tools-dev tools/boostbook/dtd/* usr/share/boostbook/dtd

	# package libboost-date-time$(PKGVERSION)-dev
	dh_installdocs -plibboost-date-time$(PKGVERSION)-dev libs/date_time/data

	find debian/tmp -iname '*.so.*' -type f -print0 | xargs -0 -r chrpath --delete
	dh_install

override_dh_install-indep:
	# package libboost-doc
	mkdir -p debian/libboost$(PKGVERSION)-doc/usr/share/doc/libboost$(PKGVERSION)-doc
	cp -a doc debian/libboost$(PKGVERSION)-doc/usr/share/doc/libboost$(PKGVERSION)-doc
	# provide a constant symlink to the latest documents and examples
	dh_link -plibboost$(PKGVERSION)-doc \
	   usr/share/doc/libboost$(PKGVERSION)-doc/doc \
	   usr/share/doc/libboost-doc/doc
	dh_link -plibboost$(PKGVERSION)-doc \
	   usr/share/doc/libboost$(PKGVERSION)-doc/examples \
	   usr/share/doc/libboost-doc/examples

	mkdir -p $(exampledir)
	cat debian/example-files | xargs cp -a --parents --target-directory=$(exampledir)
	find $(exampledir) -type f | xargs chmod 644

override_dh_makeshlibs:
	dh_makeshlibs
	sed -i -r 's/^(libboost_python([0-9]+) \S+ (\S+).*)$$/\1, \3-py\2/' debian/libboost-python$(SOVERSION)/DEBIAN/shlibs
	sed -i -r 's/^(libboost_mpi_python([0-9]+) \S+ (\S+).*)$$/\1, \3-py\2/' debian/libboost-mpi-python$(SOVERSION)/DEBIAN/shlibs
ifeq ($(BUILD_NUMPY), yes)
	sed -i -r 's/^(libboost_numpy([0-9]+) \S+ (\S+).*)$$/\1, \3-py\2/' debian/libboost-numpy$(SOVERSION)/DEBIAN/shlibs
endif

override_dh_gencontrol:
	dh_gencontrol -- -V'gxx:major=$(shell dpkg-query -f '$${version}' -W g++ | sed 's/.*://;s/\..*//')'

clean-debhelper:
	rm -rf debian/*.install
	rm -rf debian/*.links
	rm -rf debian/*.lintian-overrides

# Make all the generated debhelper files.
make-debhelper: clean-debhelper
	@$(call mk_debhelper_files)
	@$(call cp_debhelper,libboost-dev,libboost$(PKGVERSION)-dev)
	@$(call cp_debhelper,libboost-doc,libboost$(PKGVERSION)-doc)
	@$(call cp_debhelper,libboost-python-dev,libboost-python$(PKGVERSION)-dev)
	@$(call cp_debhelper,libboost-mpi-python-dev,libboost-mpi-python$(PKGVERSION)-dev)
