#!/usr/bin/make -f

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

# See debhelper(7) (uncomment to enable)
# output every command that modifies files on the build system.
#export DH_VERBOSE = 1

# python packages do not contain any tests
export PYBUILD_DISABLE=test

export MAKEFLAGS = --no-print-directory

# helper to compare strings
strequ=$(if $(subst x$1,,x$2)$(subst x$2,,x$1),,1)

bold := $(shell which tput >/dev/null 2>&1 && tput bold 2>/dev/null)
sgr0 := $(shell which tput >/dev/null 2>&1 && tput sgr0 2>/dev/null)

# special characters
empty :=
space := $(empty) $(empty)
comma := ,

# helper to create comma separated list from space separated list
commasep = $(subst $(space),$(comma)$(space),$1)
# recursive wildcard
rwildcard=$(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))

# remove double quotes
unquote = $(subst $\",,$1)

# set these variables to define build of certain boards/scenarios, e.g.
ACRN_BOARDLIST ?=
ACRN_SCENARIOLIST ?=

# for now build the debug versions
# set to y for RELEASE build
export RELEASE ?= n

# eventually add-in locally contributed configurations
-include debian/configs/configurations.mk
debian/configs/configurations.mk:
	@:

# misc/config_tools/data: contains ACRN supported configuration
# debian/configs: add additional, unsupported configurations here!
CONFIGDIRS ?= misc/config_tools/data debian/configs
CONFIGXMLS := $(call rwildcard,$(CONFIGDIRS),*.xml)

# get relevant data from acrn-config attributes of an XML
#
# This evaluates the attributes of acn-config tag got from xmllint (after unquoting).
# It create the following variables:
#  * boardlist: accumulated list of all board configurations detected
#  * scenariolist_<board>: accumulated list of sceanrios for the given <board>
#  * config_<board>: respective board configuration file
#  * config_<board>_<scenario>: respective scenario configuration file
define get-xml-data
$(eval $(call unquote,$(shell xmllint --xpath '/acrn-config/@board' $1 2>/dev/null)))
$(eval $(if $(board),                                                                     \
    $(eval config_$(board) := $1)                                                         \
    $(eval boardlist := $(sort $(boardlist) $(board)))                                    \
    $(foreach f,$(wildcard $(addprefix $(dir $1),*.xml)),                                 \
        $(if $(strip $(shell xmllint --xpath '/acrn-config/@board' $f 2>/dev/null)),,     \
            $(if $(subst scenario.xml,,$(notdir $f)),                                     \
                $(eval scenario = $(basename $(notdir $f))),                              \
                $(eval scenario = $(notdir $(abspath $(dir $f)))))                        \
            $(eval config_$(board)_$(scenario) := $f)                                     \
            $(eval scenariolist_$(board) := $(sort $(scenariolist_$(board)) $(scenario))) \
        )                                                                                 \
    )                                                                                     \
))
$(eval undefine scenario)
$(eval undefine board)
endef

# get all XML data
$(foreach xml,$(CONFIGXMLS),$(call get-xml-data,$(xml)))

# honor variable ACRN_BOARDLIST by filtering the discovered board configurations accordingly
ifneq ($(ACRN_BOARDLIST),)
boardlist := $(sort $(filter $(boardlist),$(ACRN_BOARDLIST)))
endif

# honor variable ACRN_SCENARIOLIST by filtering the discovered scenario configurations
ifneq ($(ACRN_SCENARIOLIST),)
$(foreach b,$(boardlist),$(eval scenariolist_$(b) := $(filter $(scenariolist_$(b)),$(ACRN_SCENARIOLIST))))
endif

# only keep boards with at least one valid scenario configuration
boardlist := $(foreach b,$(boardlist),$(if $(scenariolist_$(b)),$b))
# assert nonempty boardlist
ifeq ($(boardlist),)
$(error No valid board/scenario configurations specified/found)
endif

# board config name -> board config file
bfile = $(abspath $(config_$1))
# board/scenario config name -> scenario config file
sfile = $(abspath $(config_$1_$2))

# uncomment for debugging purposes
# $(info boardlist: $(boardlist))
# $(foreach b,$(boardlist),$(info scenariolist_$(b): $(scenariolist_$(b))))
# $(foreach b,$(boardlist),$(info config_$(b): $(config_$(b))))
# $(foreach b,$(boardlist),$(foreach s,$(scenariolist_$(b)),$(info config_$(b)_$(s): $(config_$(b)_$(s)))))

# get version data for ACRN
include VERSION
export ACRNVERSION := $(MAJOR_VERSION).$(MINOR_VERSION)

# set paths (see also paths.make)
export prefix = /usr
export bindir = $(prefix)/bin
export libdir = $(prefix)/lib/$(DEB_HOST_MULTIARCH)
export nonarchlibdir = $(prefix)/lib
export datadir = $(prefix)/share
export includedir = $(prefix)/include
export systemd_unitdir = /lib/systemd
export docdir = $(datadir)/doc
export sysconfdir = /etc

export BUILD_VERSION = "$(DEB_VERSION_UPSTREAM)"
export BUILD_TAG = "$(DEB_VENDOR)/$(DEB_VERSION)"

# set iASL compiler
ifeq ($(ASL_COMPILER),)
export ASL_COMPILER:=$(shell which iasl)
ifeq ($(ASL_COMPILER),)
$(error ASL_COMPILER missing)
endif
endif

# let make verbosity be controlled by DH_VERBOSE
ifeq ($(DH_VERBOSE),1)
  Q=
  echo-verbose = echo "   $@$(if $1,: $(bold)$1$(sgr0))"
  echo-silent = true
  devnull =
else
  Q=@
  echo-verbose = true
  echo-silent = echo "   $@$(if $1,: $(bold)$1$(sgr0))"
  devnull = >/dev/null
endif

### dh_installdeb ############################################################

# create acrntools.install for debug build
ifeq ($(RELEASE),n)

debian/acrn-tools.install: debian/acrn-tools.install.debug
	@$(call echo-verbose)
	@$(call echo-silent,CREATE $@)
	$(Q)cp $< $@

override_dh_install: debian/acrn-tools.install
	$(Q)dh_install

endif

### dh_installdeb ############################################################
debian/acrn-hypervisor.postinst: debian/acrn-hypervisor.postinst.in
	@$(call echo-verbose)
	@$(call echo-silent,CREATE $@)
	$(Q)sed -e 's/@acrnversion@/$(ACRNVERSION)/g' \
		-e 's%@acrndir@%$(libdir)/acrn%g' $< > $@

debian/acrn-hypervisor.postrm: debian/acrn-hypervisor.postrm.in
	@$(call echo-verbose)
	@$(call echo-silent,CREATE $@)
	$(Q)sed -e 's/@acrnversion@/$(ACRNVERSION)/g' $< > $@

debian/acrn-hypervisor.prerm: debian/acrn-hypervisor.prerm.in
	@$(call echo-verbose)
	@$(call echo-silent,CREATE $@)
	$(Q)sed -e 's/@acrnversion@/$(ACRNVERSION)/g' $< > $@

override_dh_installdeb: debian/acrn-hypervisor.postinst debian/acrn-hypervisor.postrm debian/acrn-hypervisor.prerm
	$(Q)dh_installdeb

### dh_installdebconf-arch ###################################################
debian/acrn-hypervisor.templates: debian/acrn-hypervisor.templates.in
	@$(call echo-verbose)
	@$(call echo-silent,CREATE $@)
	$(Q)sed -e 's/@boardlist@/$(call commasep,$(boardlist))/g' $< > $@

debian/acrn-hypervisor.config: debian/acrn-hypervisor.config.in
	@$(call echo-verbose)
	@$(call echo-silent,CREATE $@)
	@# create scenarios as bash associative array
	$(Q)sed -e 's%@acrndir@%$(libdir)/acrn%g' \
	        -e 's%@scenarios@%($(foreach b,$(boardlist),[$b]="$(call commasep,$(scenariolist_$b))"))%g' $< > $@

override_dh_installdebconf-arch: debian/acrn-hypervisor.templates debian/acrn-hypervisor.config
	$(Q)dh_installdebconf -a

### build-arch ###############################################################
override_dh_auto_build-arch: O=build
override_dh_auto_build-arch:
	@$(call echo-verbose)
	@# Only build lifemngr here, since other parts are built during
	@# dh_auto_install (even if built here). This is deficiency
	@# of ACRN's makefile structure, e.g. make hypervisor-install
	@# always rebuilds the hypervisor, even if built with
	@# make hypervisor in a previous step.
	$(Q)set -e; $(call echo-silent,BUILD life_mngr)
	$(Q)mkdir -p $(O)/common/misc
	$(Q)$(MAKE) -C misc						\
		OUT_DIR=$(abspath $(O)/common/misc)			\
		life_mngr $(devnull)

### build-indep ##############################################################
ifeq ($(EXTRA_VERSION),)
ACRN_DOC_VERSION := $(DEB_VERSION_UPSTREAM)
else
ACRN_DOC_VERSION := latest
endif

build/documentation/html/index.html: debian/acrn-doc-index-html.in
	@$(call echo-verbose)
	@$(call echo-silent,BUILD documentation)
	$(Q)mkdir -p $(@D)
	$(Q)sed -e 's/@acrnversion@/$(ACRN_DOC_VERSION)/g' $< > $@

override_dh_auto_build-indep: build/documentation/html/index.html
	@$(call echo-verbose)
	@$(call echo-silent,BUILD acrn-board-inspector)
	$(Q)export PYBUILD_NAME=acrn_board_inspector; \
	dh_auto_build \
	    --buildsystem=pybuild \
	    --package=python3-acrn-board-inspector \
	    --sourcedirectory=debian/acrn-board-inspector $(devnull)

### binary-arch ##############################################################

# check_post_launched <configfile>
# Checks if there is a POST_LAUNCHED_VM configured in the given config file
# returns empty string (i.e. false) if there is no POST_LAUNCHED_VM detected
check_post_launched=$(strip $(shell xmllint --xpath '//vm[load_order="POST_LAUNCHED_VM"]/@id' $1 2>/dev/null || true))

# helper to determine if there are any POST_LAUNCHED VMs in any configuration
check_any_post_launched=$(strip $(foreach b,$(boardlist),$(foreach s,$(scenariolist_$b),$(call check_post_launched,$(call sfile,$b,$s)))))

# dynamically generate acrn-hypervisor.install to conditionally add launch script directory
debian/acrn-hypervisor.install:
	@$(call echo-verbose)
	@$(call echo-silent,CREATE $@)
	$(Q)echo "usr/lib/x86_64-linux-gnu/acrn/*" >$@
	$(Q)if [ -n "$(call check_any_post_launched)" ]; then echo "usr/share/acrn/launch-scripts/" >>$@; fi

.PHONY: debian/acrn-hypervisor.install

override_dh_auto_install-arch: O=build
override_dh_auto_install-arch: DESTDIR=$(abspath debian/tmp)
override_dh_auto_install-arch: debian/acrn-hypervisor.install
	@$(call echo-verbose)
	$(Q)set -e;$(foreach b,$(boardlist),$(foreach s,$(scenariolist_$b),         \
		$(call echo-silent,BUILD & INSTALL hypervisor for $b:$s);           \
		$(MAKE) O=$(O)/$b/$s                                                \
			BOARD=$(call bfile,$b)                                      \
			SCENARIO=$(call sfile,$b,$s)                                \
			DESTDIR=$(DESTDIR)				            \
			hypervisor-install $(devnull);			            \
		install -d $(DESTDIR)$(libdir)/acrn/$b/$s;		            \
		rm -f $(DESTDIR)$(libdir)/acrn/*.out;			            \
		mv $(DESTDIR)$(libdir)/acrn/*.bin			            \
			$(DESTDIR)$(libdir)/acrn/$b/$s;			            \
		if [ -d $(DESTDIR)$(sysconfdir) ]; then			            \
			mv $(DESTDIR)$(sysconfdir)			            \
				$(DESTDIR)$(libdir)/acrn/$b/$s;		            \
		fi;							            \
		if [ -d $(DESTDIR)$(libdir)/acrn/acpi ]; then		            \
			mv $(DESTDIR)$(libdir)/acrn/acpi		            \
				$(DESTDIR)$(libdir)/acrn/$b/$s;		            \
		fi;							            \
		cp $(O)/$b/$s/hypervisor/configs/config.mk		            \
			$(DESTDIR)$(libdir)/acrn/$b/$s/acrn.$b.$s.config;           \
		cp $(O)/$b/$s/hypervisor/acrn.map			            \
			$(DESTDIR)$(libdir)/acrn/$b/$s/acrn.$b.$s.map;	            \
		chmod 644 $(DESTDIR)$(libdir)/acrn/$b/$s/*.bin;		            \
		if [ -d $(DESTDIR)$(libdir)/acrn/$b/$s/acpi ]; then                 \
			chmod 644 $(DESTDIR)$(libdir)/acrn/$b/$s/acpi/*;            \
		fi;                                                                 \
		cp $(call sfile,$b,$s) $(DESTDIR)$(libdir)/acrn/$b/$s/scenario.xml; \
		if [ -n "$(call check_post_launched,$(call sfile,$b,$s))" ]; then   \
			$(call echo-silent,CREATE launch scripts in $(datadir)/acrn/launch-scripts/$b/$s/); \
			mkdir -p $(DESTDIR)$(datadir)/acrn/launch-scripts/$b/$s/;   \
			misc/config_tools/launch_config/launch_cfg_gen.py           \
			    --board $(call bfile,$b) --scenario $(call sfile,$b,$s) \
			    --out $(DESTDIR)$(datadir)/acrn/launch-scripts/$b/$s    \
			    $(devnull) 2>&1;                          \
			chmod a+x $(DESTDIR)$(datadir)/acrn/launch-scripts/$b/$s/*; \
		fi;                                                                 \
	) cp $(call bfile,$b) $(DESTDIR)$(libdir)/acrn/$b/board.xml;)
	@:
	@# install devicemodel & tools
	@$(call echo-silent,BUILD & INSTALL devicemodel & tools)
	$(Q)$(MAKE) O=$(O)/common DESTDIR=$(DESTDIR) devicemodel-install $(devnull)
	$(Q)$(MAKE) O=$(O)/common DESTDIR=$(DESTDIR) tools-install $(devnull)
	@:
	@# install life_mngr
	@$(call echo-silent,INSTALL life_mngr)
	$(Q)mkdir -p $(O)/common/misc
	$(Q)$(MAKE) -C misc                             \
		DESTDIR=$(DESTDIR)                      \
		OUT_DIR=$(abspath $(O)/common/misc)     \
		acrn-life-mngr-install $(devnull)
	@# rename to acrn-lifemngr
	$(Q)mv $(DESTDIR)$(bindir)/life_mngr $(DESTDIR)$(bindir)/acrn-lifemngr
	@# install config file templates
	$(Q)install -d $(DESTDIR)$(datadir)/acrn-lifemngr
	$(Q)mv $(DESTDIR)$(sysconfdir)/life_mngr/life_mngr.conf \
	       $(DESTDIR)$(datadir)/acrn-lifemngr/life_mngr.conf.service_vm
	$(Q)install -m 0644 debian/lifemngr/life_mngr.conf.user_vm $(DESTDIR)$(datadir)/acrn-lifemngr
	@# install invocation helper
	$(Q)install -m 0755 debian/lifemngr/start-acrn-lifemngr.sh $(DESTDIR)$(datadir)/acrn-lifemngr
	@# adapt life_mngr.service
	$(Q)mv $(DESTDIR)$(systemd_unitdir)/system/life_mngr.service \
	       $(DESTDIR)$(systemd_unitdir)/system/acrn-lifemngr.service
	$(Q)sed -i -e 's#^ExecStart=.*#ExecStart=$(datadir)/acrn-lifemngr/start-acrn-lifemngr.sh#' \
	        $(DESTDIR)$(systemd_unitdir)/system/acrn-lifemngr.service
	@:
	@# move/adapt network configs as examples only
	$(Q)mkdir -p $(DESTDIR)$(docdir)/acrnd/examples
	$(Q)cp $(DESTDIR)$(systemd_unitdir)/network/*				\
		$(DESTDIR)$(docdir)/acrnd/examples
	$(Q)sed -i 's/acrn_tap\* //g'						\
		$(DESTDIR)$(docdir)/acrnd/examples/50-acrn.network
	@:
	@# adapt systemd services for tools (available in DEBUG build only)
	$(if $(call strequ,${RELEASE},n),					\
		$(Q)sed -i -e '/telemd.socket/d' -e '/prepare.service/d'	\
			$(DESTDIR)$(systemd_unitdir)/system/acrnprobe.service;	\
		sed -i '/telemd/d'						\
			$(DESTDIR)$(systemd_unitdir)/system/usercrash.service;	\
		install -d $(DESTDIR)$(datadir)/defaults/telemetrics;		\
		install -m 0644 -t $(DESTDIR)$(datadir)/defaults/telemetrics	\
			misc/debug_tools/acrn_crashlog/data/acrnprobe.xml;	\
		install -d $(DESTDIR)$(datadir)/acrn/scripts;			\
		cp -a misc/debug_tools/acrn_trace/scripts/*			\
			$(DESTDIR)$(datadir)/acrn/scripts;			\
		install -d $(DESTDIR)$(sysconfdir)/default;			\
		cp debian/acrnlog/acrnlog.default				\
			$(DESTDIR)$(sysconfdir)/default/acrnlog;		\
		sed -i -e 's#ExecStart=.*#EnvironmentFile=$(sysconfdir)/default/acrnlog\nExecStart=$(bindir)/acrnlog $${ACRNLOG_ARGS}#' \
			$(DESTDIR)$(systemd_unitdir)/system/acrnlog.service	\
	)

### binary-indep #############################################################
override_dh_auto_install-indep: O=build
override_dh_auto_install-indep: DESTDIR=$(abspath debian/tmp)
override_dh_auto_install-indep:
	@$(call echo-verbose)
	@$(call echo-silent,INSTALL acrn-board-inspector)
	$(Q)export PYBUILD_NAME=acrn_board_inspector; \
	dh_auto_install \
	    --buildsystem=pybuild \
	    --package=python3-acrn-board-inspector \
	    --sourcedirectory=debian/acrn-board-inspector $(devnull)
	@:
	@$(call echo-silent,INSTALL documentation)
	$(Q)mkdir -p ${DESTDIR}$(docdir)/acrn/
	$(Q)cp -a $(O)/documentation/html ${DESTDIR}$(docdir)/acrn/

### dh_missing ###############################################################
override_dh_missing:
	$(Q)dh_missing --fail-missing

### dh_installsystemd ########################################################
# do not start services at install, since they are most likely installed on a
# non-ACRN system
override_dh_installsystemd:
	$(Q)dh_installsystemd --no-start

### dh_strip #################################################################
# only strip release variant binaries
override_dh_strip: DESTDIR=$(abspath debian/tmp)
override_dh_strip:
	$(Q)dh_strip -X.out -X.bin
	$(Q)strip --strip-debug							\
		--remove-section=.comment					\
		--remove-section=.note						\
		--enable-deterministic-archives					\
		$(DESTDIR)$(libdir)/libacrn-mngr.a;

### clean ####################################################################
override_dh_auto_clean:
	@echo "Building ACRN $(ACRNVERSION) for boards: $(bold)$(call commasep,$(boardlist))$(sgr0)"
	@echo "The scenarios used are:"
	@$(foreach b,$(boardlist),echo "  $b: $(bold)$(call commasep,$(scenariolist_$b))$(sgr0)";)
	@$(call echo-verbose)
	@$(call echo-silent,CLEAN)
	$(Q)rm -rf .pybuild/
	$(Q)find . -type d -name __pycache__ | xargs rm -rf
	$(Q)rm -f debian/acrn-hypervisor.install
	$(Q)rm -f debian/acrn-hypervisor.config
	$(Q)rm -f debian/acrn-hypervisor.postinst
	$(Q)rm -f debian/acrn-hypervisor.postrm
	$(Q)rm -f debian/acrn-hypervisor.prerm
	$(Q)rm -f debian/acrn-tools.install
	$(Q)rm -f debian/acrn-hypervisor.templates
	$(Q)rm -rf debian/acrn-board-inspector/build
	$(Q)rm -rf debian/acrn-board-inspector/acrn_board_inspector.egg-info
	$(Q)dh_auto_clean $(devnull)

### others ###################################################################
%:
	$(Q)dh $@ --with python3
