#
# third-party-cpp-libs project main makefile
# Author: fmontorsi/aferrari
#

REALTOP=$(shell readlink -f .)
TOPDIR=.

include ${TOPDIR}/Makefiles/Repo-defines.mk
-include ${EMPIRIX_PIPELINE_FRAMEWORK_MAKEFILE_SUPPORT_DIR}/Version-defines.mk
-include ${EMPIRIX_PIPELINE_FRAMEWORK_MAKEFILE_SUPPORT_DIR}/Deploy-defines.mk


# ------------------------------------------------------------------------------------------
# Constants
# ------------------------------------------------------------------------------------------

# libibverbs and libnl3 and libmnl are disabled since we bought the 6Windgate product
DIRECTORIES_DISABLED= \
	libibverbs        \
	libmnl            \
	libnl3            \
	rtsp-parser


DIRECTORIES_EVA_ECC_SW=\
	6wind             \
	backward-cpp      \
	boost_intrusive_pool \
	cpp-base64        \
	fasthash          \
	highsee           \
	hiredis           \
	iiiasn            \
	moodycamel        \
	ixEngine-5.8.0-52 \
	malloc_tag        \
	nubeva-empirix    \
	osip              \
	oss               \
	pcap              \
	pulsar-client-cpp \
	restbed           \
	rdkafka           \
	version           \
	vqmon-4.2         \
	xmlParser         \
	zmq               \
	rtp-codecs        \
	prometheus-cpp    \
	nghttp2	       	  \
	picojson

#
# NOTE: unfortunately the order of DIRECTORIES is important here:
#       ucarp depends on libpcap, thus "pcap" folder needs to come first!

dpdk := pcap
#libibverbs := libnl3
#zmq := sodium
#czmq := uuid zmq
#zyre := czmq
#pulsar-client-cpp := curl

ifeq ($(ARCH), x86_64)
DIRECTORIES=$(DIRECTORIES_EVA_ECC_SW)
endif
ifeq ($(ARCH), docker)
DIRECTORIES=$(DIRECTORIES_EVA_ECC_SW)
endif



# ------------------------------------------------------------------------------------------
# Definition of recursive targets: "all", "clean", "distclean" etc
# ------------------------------------------------------------------------------------------

SUPPORTS_CPPCHECK=0
HAS_FULLDIST_IN_BUILDER_DOCKER=1
HAS_UNIT_TESTS_IN_BUILDER_DOCKER=0
HAS_DOCUMENTATION_IN_BUILDER_DOCKER=0
HAS_FULLPACKAGING_IN_BUILDER_DOCKER=1
BUILDER_DOCKER_MAKE_OUTPUT_MODE=target
HAS_PACKAGING=2 # custom implementation in this makefile
-include ${EMPIRIX_PIPELINE_FRAMEWORK_MAKEFILE_SUPPORT_DIR}/Recursive-targets.mk


# ------------------------------------------------------------------------------------------
# Check for dependencies
# ------------------------------------------------------------------------------------------

ifeq ($(REMOTE_CONAN_NAME_FOR_PULL),)
REMOTE_CONAN_NAME_FOR_PULL:=nexus_proxy
endif

check_deps_pipeline:
ifeq (,$(CONAN_DEPENDENCIES_DIR_PIPELINE))
	@echo "ERROR: must define CONAN_DEPENDENCIES_DIR_PIPELINE (usually in your local Makefiles/Repo-defines.mk). Aborting."
	@exit 123
endif
	@mkdir -p $(CONAN_DEPENDENCIES_DIR_PIPELINE) && rm -rf $(CONAN_DEPENDENCIES_DIR_PIPELINE)/*
	@if ! conan remote list | grep $(REMOTE_CONAN_NAME_FOR_PULL) 2>/dev/null 1>&2; then \
		echo "ERROR: there is no Conan remote repository named [$(REMOTE_CONAN_NAME_FOR_PULL)]. Please run setup-devel-centos7.sh again." ; \
		exit 123 ; \
	fi
	# to make life easier to developers, ask Conan to check dependencies for both ARCH=x86_64 and ARCH=docker
	@echo "------------------------------------------------------------------------------------"
	@echo "Checking pipeline dependencies for ARCH=docker"
	@echo "------------------------------------------------------------------------------------"
	mkdir -p $(CONAN_DEPENDENCIES_DIR_PIPELINE) && \
		cd $(CONAN_DEPENDENCIES_DIR_PIPELINE) && \
		conan install ../.. -e INSTALL_PIPELINE_ONLY=True

check_deps:
	$(MAKE) check_deps_upstream

full_check_deps:
	$(MAKE) check_deps_pipeline
	$(MAKE) check_deps_upstream CFG=release RUNTIME=$(RUNTIME)
	$(MAKE) check_deps_upstream CFG=debug-gcov RUNTIME=$(RUNTIME)

# ------------------------------------------------------------------------------------
# clean_build_intermediate_artifacts TARGET
# ------------------------------------------------------------------------------------

# create install_libs targets using dependencies
define clean_build_intermediate_artifacts
$(1).CLEAN_INTERMEDIATE:$(foreach dependency,$($(1)),$(dependency).CLEAN_INTERMEDIATE)
	$$(MAKE) --directory=$(1) clean_build_intermediate_artifacts
endef

# set up install_libs targets
$(foreach dir,$(DIRECTORIES),$(eval $(call clean_build_intermediate_artifacts,$(dir))))

# set lists of all the target dirs
CLEAN_INTERMEDIATE_DIRS := $(foreach dir,$(DIRECTORIES),$(dir).CLEAN_INTERMEDIATE)

clean_build_intermediate_artifacts: $(CLEAN_INTERMEDIATE_DIRS)
	@echo ""
	@echo "Done regenerating: $(DIRECTORIES)"
	@echo ""
	
.PHONY: clean_build_intermediate_artifacts $(INSTALL_LIBS_DIRS)
# ------------------------------------------------------------------------------------

# ------------------------------------------------------------------------------------
# install_libs TARGET
# ------------------------------------------------------------------------------------

# create install_libs targets using dependencies
define install_libs
$(1).INSTALL_LIBS:$(foreach dependency,$($(1)),$(dependency).INSTALL_LIBS)
	$$(MAKE) --directory=$(1) install_libs
endef

# set up install_libs targets
$(foreach dir,$(DIRECTORIES),$(eval $(call install_libs,$(dir))))

# set lists of all the target dirs
INSTALL_LIBS_DIRS := $(foreach dir,$(DIRECTORIES),$(dir).INSTALL_LIBS)

install_libs: $(INSTALL_LIBS_DIRS)
	@echo ""
	@echo "Done regenerating: $(DIRECTORIES)"
	@echo ""
	
.PHONY: install_libs $(INSTALL_LIBS_DIRS)
# ------------------------------------------------------------------------------------



# ------------------------------------------------------------------------------------
# install_headers TARGET
# ------------------------------------------------------------------------------------
# 

# create install_headers targets using dependencies
define install_headers
$(1).INSTALL_HEADERS:$(foreach dependency,$($(1)),$(dependency).INSTALL_HEADERS)
	$$(MAKE) --directory=$(1) install_headers
endef

# set up install_headers targets
$(foreach dir,$(DIRECTORIES),$(eval $(call install_headers,$(dir))))

# set lists of all the target dirs
INSTALL_HEADERS_DIRS := $(foreach dir,$(DIRECTORIES),$(dir).INSTALL_HEADERS)

install_headers: $(INSTALL_HEADERS_DIRS)
	@echo ""
	@echo "Done regenerating: $(DIRECTORIES)"
	@echo ""
	
.PHONY: install_headers $(INSTALL_HEADERS_DIRS)
# ------------------------------------------------------------------------------------

# ------------------------------------------------------------------------------------
# install ixengine license TARGET
# ------------------------------------------------------------------------------------
install_ixengine_license:
ifndef DESTDIR
	@echo "*** ERROR: please call this makefile supplying explicitly the DESTDIR variable"
	@exit 1
endif
	mkdir -p $(DESTDIR)
	cp $(TOPDIR)/ixEngine-5.8.0-52/Q2300016-20230210.bin       $(DESTDIR)/

# ------------------------------------------------------------------------------------

# ------------------------------------------------------------------------------------
# install highsee config files
# ------------------------------------------------------------------------------------
install_highsee_config_files:
ifndef DESTDIR
	@echo "*** ERROR: please call this makefile supplying explicitly the DESTDIR variable"
	@exit 1
endif
	mkdir -p $(DESTDIR)/highsee/etc
	mkdir -p $(DESTDIR)/highsee/conf
	cp $(TOPDIR)/highsee/etc/*       $(DESTDIR)/highsee/etc
	cp -ar $(TOPDIR)/highsee/conf/*  $(DESTDIR)/highsee/conf

# ------------------------------------------------------------------------------------
# install_support_makefiles TARGET
# ------------------------------------------------------------------------------------
#
install_support_makefiles:
ifndef DESTDIR
	@echo "*** ERROR: please call this makefile supplying explicitly the DESTDIR variable"
	@exit 1
endif
	mkdir -p $(DESTDIR)
	# old deprecated naming for support makefiles:
	cp -ar $(TOPDIR)/*/*-support                                      $(DESTDIR)/
	# new naming of support makefiles:
	cp -ar $(TOPDIR)/*/*-support*.mk                                  $(DESTDIR)/
	# other support makefiles for the whole repo:
	cp $(TOPDIR)/Packaging/Makefile.thirdparty-cpp-libs-version       $(DESTDIR)/
	cp $(TOPDIR)/Packaging/ThirdPartyCppLibs-tarball-targets.mk       $(DESTDIR)/

# ------------------------------------------------------------------------------------
# check_shared_libraries TARGET
# ------------------------------------------------------------------------------------

check_shared_libraries:
	CROSSTOOLDIR="$(CROSSTOOLDIR)" \
	RUNTIME_DIR="$(RUNTIME_DIR)" \
	TOOLCHAIN_GCC_VERSION="$(TOOLCHAIN_GCC_VERSION)" \
		./check-shared-libraries.sh $(foreach dir, $(DIRECTORIES),$(dir)/$(BUILDID)/*.so.? $(DIRECTORIES),$(dir)/$(BUILDID)/*.so.?.? $(DIRECTORIES),$(dir)/$(BUILDID)/*.so.?.?.?)
	@echo ""
	@echo "Done checking: $(DIRECTORIES)"
	@echo ""
# ------------------------------------------------------------------------------------
	


# ------------------------------------------------------------------------------------
# symlinks_to_destdir TARGET
# ------------------------------------------------------------------------------------
#
# In case you want to run Empirix software without installing RPMs, it is useful
# to have all dynamic libraries in a single directory, so that you can 
#    export LD_LIBRARY_PATH=Third-Party/cpp-libs/Lib
# and run the software. To do so, you can run
#     make symlinks_to_destdir DESTDIR=/your/dir
# to create symlinks_to_destdir of shared libraries in the folder you want
#

REALTOP=$(shell readlink -f $(TOPDIR))
ifndef SYMLINK_DESTDIR
SYMLINK_DESTDIR=$(REALTOP)/cpp-libs/Lib/$(BUILDID)
endif


# create symlinks_to_destdir targets using dependencies
define symlinks_to_destdir
$(1).SYMLINKS_TO_DESTDIR:$(foreach dependency,$($(1)),$(dependency).SYMLINKS_TO_DESTDIR)
	$$(MAKE) --directory=$(1) symlinks_to_destdir DESTDIR=$(SYMLINK_DESTDIR)
endef

# set up symlinks_to_destdir targets
$(foreach dir,$(DIRECTORIES),$(eval $(call symlinks_to_destdir,$(dir))))
SYMLINKS_TO_DESTDIR_DIRS := $(foreach dir,$(DIRECTORIES),$(dir).SYMLINKS_TO_DESTDIR)

symlinks_to_destdir: $(SYMLINKS_TO_DESTDIR_DIRS)
	@echo ""
	@echo "Done symlinking: $(DIRECTORIES)"
	@echo ""
	
.PHONY: symlinks_to_destdir $(SYMLINKS_TO_DESTDIR_DIRS)

# ------------------------------------------------------------------------------------

# ------------------------------------------------------------------------------------
# strip_libs TARGET
# ------------------------------------------------------------------------------------

# create strip targets using dependencies
define strip_libs
$(1).STRIP_LIBS:$(foreach dependency,$($(1)),$(dependency).STRIP_LIBS)
	$$(MAKE) --directory=$(1) strip
endef

# set up strip_libs targets
$(foreach dir,$(DIRECTORIES),$(eval $(call strip_libs,$(dir))))
STRIP_DIRS := $(foreach dir,$(DIRECTORIES),$(dir).STRIP_LIBS)

strip_libs: $(STRIP_DIRS)
	@echo ""
	@echo "Done stripping inside: $(DIRECTORIES)"
	@echo ""
	
.PHONY: strip_libs $(STRIP_DIRS)

# ------------------------------------------------------------------------------------

# ------------------------------------------------------------------------------------
# remove_non_cpp_libs TARGET
# ------------------------------------------------------------------------------------

define SHELL_SCRIPT_CONTENT
DIRLIST=`find -maxdepth 1 -type d | tr '\n' ' ' | tr -d '/' | tr -d '.'`;
for dir in $$DIRLIST ; do
     if [[ "$(DIRECTORIES) $(DIRECTORIES_NOT_BUILDING_WITH_NEW_TOOLCHAIN)" =~ .*"$$dir".* ]]; then
          echo "$$dir is in the list... nothing to do" ;
     else
          p4 delete $$dir/...
     fi
done
endef
export SHELL_SCRIPT_CONTENT

remove_non_cpp_libs:
	@echo ""
	@echo "Removing directories that are not part of this list: $(DIRECTORIES) $(DIRECTORIES_NOT_BUILDING_WITH_NEW_TOOLCHAIN)"
	@echo ""
	echo "$$SHELL_SCRIPT_CONTENT" > remove_non_cpp_libs.sh
	chmod a+x remove_non_cpp_libs.sh
	/bin/bash -c "./remove_non_cpp_libs.sh"
	
.PHONY: remove_non_cpp_libs
# ------------------------------------------------------------------------------------

# --------------
# Shell commands
# --------------

BUILDER_IMAGE_TAG=1
BUILDER_IMAGE_TYPE=standard

bash:
	@echo "Updating builder image: infovista/cpp-builder-ol9-$(BUILDER_IMAGE_TYPE):$(BUILDER_IMAGE_TAG)"
	@docker pull -q ghcr.io/infovista/cpp-builder-ol9-$(BUILDER_IMAGE_TYPE):$(BUILDER_IMAGE_TAG)
	@echo "Starting builder image: infovista/cpp-builder-ol9-$(BUILDER_IMAGE_TYPE):$(BUILDER_IMAGE_TAG)"
	@docker run --rm -ti  \
		--name builder_bash_baselibs \
        --volume ~/.docker_builder_bash_history:/root/.bash_history:rw \
        --network=host --privileged \
        --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
        --volume `pwd`:`pwd` \
        --volume /opt/empirix/conan:/opt/empirix/conan \
        --volume /var/run/docker.sock:/var/run/docker.sock \
        --volume /etc/docker/daemon.json:/etc/docker/daemon.json \
        --volume ~/.config/helm/repositories.yaml:/root/.config/helm/repositories.yaml \
        --volume ~/.docker/config.json:/root/.docker/config.json \
        --volume ~/.conan:/root/.conan \
        --volume /etc/yum/vars/artifactory-yum-repository:/etc/yum/vars/artifactory-yum-repository \
        --mount type=tmpfs,destination=/home/builder/rpmbuild \
        --workdir=`pwd` \
        --env TEAMCITY_VERSION="" \
        --env BUILD_AGENT="0" \
        ghcr.io/infovista/cpp-builder-ol9-$(BUILDER_IMAGE_TYPE):$(BUILDER_IMAGE_TAG) || true

# ------------------------------------------------------------------------------------------
# fulldist target
# ------------------------------------------------------------------------------------------

fulldist:
ifeq ($(ARCH), docker)
	$(MAKE) full_check_deps
	$(MAKE) all CFG=release  # enter all subfolders recursively to build binaries with docker arch
	$(MAKE) all CFG=debug-gcov    # enter all subfolders recursively to build binaries with docker arch
	# use Jinja2 to produce a Makefile snippet to inform the build system of downstream projects about our version
	RPM_VERSION=$(RPM_VERSION) JAVA_FULL_VERSION=$(JAVA_FULL_VERSION) PYTHON_FULL_VERSION=$(PYTHON_FULL_VERSION) CONAN_VERSION=$(CONAN_VERSION) \
		j2 Packaging/Makefile.thirdparty-cpp-libs-version.j2  -o Packaging/Makefile.thirdparty-cpp-libs-version
	BUILD_TIMESTAMP="$(BUILD_TIMESTAMP)" \
        j2 Packaging/ThirdPartyCppLibs-tarball-targets.mk.j2  \
        -o Packaging/ThirdPartyCppLibs-tarball-targets.mk

	@echo "Fulldist completed successfully!"
else  # !ARCH=docker
	@echo "The fulldist using Empirix toolchain is now disabled. You should be using the Docker toolchain."
	@echo "Anyway you can still build individual folders (just not the fulldist) using the Empirix toolchain if you like."
endif

all::
	# use Jinja2 to produce a Makefile snippet to inform the build system of downstream projects about our version
	RPM_VERSION=$(RPM_VERSION) JAVA_FULL_VERSION=$(JAVA_FULL_VERSION) PYTHON_FULL_VERSION=$(PYTHON_FULL_VERSION) CONAN_VERSION=$(CONAN_VERSION) \
		j2 Packaging/Makefile.thirdparty-cpp-libs-version.j2  -o Packaging/Makefile.thirdparty-cpp-libs-version
	BUILD_TIMESTAMP="$(BUILD_TIMESTAMP)" \
        j2 Packaging/ThirdPartyCppLibs-tarball-targets.mk.j2  \
        -o Packaging/ThirdPartyCppLibs-tarball-targets.mk

# ------------------------------------------------------------------------------------------
# Automated tests targets
# ------------------------------------------------------------------------------------------

# In future when this repo start supporting the automated test
# we just need to uncomment below incude line
#-include ${EMPIRIX_PIPELINE_FRAMEWORK_MAKEFILE_SUPPORT_DIR}/AutomatedTests-targets.mk

automated_tests_stop:
automated_tests:
ifeq ($(ARCH),docker)
	@echo "##teamcity[blockOpened name='$(ARCH) AutomatedTests']"
	@echo "======================================================================================================================"
	@echo "== $(ARCH) Automated tests"
	@echo "======================================================================================================================"
	@echo "No automated tests are supported by this repo"
	@echo "##teamcity[blockClosed name='$(ARCH) AutomatedTests']"
endif

# ------------------------------------------------------------------------------------------
# Unit testing
# ------------------------------------------------------------------------------------------

docker_unit_tests::
	@echo "This repository has no unit tests"

# ------------------------------------------------------------------------------------------
# Docs
# ------------------------------------------------------------------------------------------

docker_documentation::
	@echo "This repository has no docs"

# ------------------------------------------------------------------------------------------
# Code coverage targets
# ------------------------------------------------------------------------------------------

docker_gcov_finalize:
	@echo "This repository has no coverage"

# ------------------------------------------------------------------------------------
# packaging targets
# ------------------------------------------------------------------------------------

ifeq ($(GIT_BRANCH_NAME),develop)
# IMPORTANT: to simplify downstream project releases, always deploy to Artifactory; REPO_TYPE_FOR_DEPLOY is used by Makefile.deploy-defines
REPO_TYPE_FOR_DEPLOY=artifactory
CONAN_CHANNEL=stable
endif

# CONAN_PACKAGES_TOREMOVE is used to cleanup the local cache and make sure we will later upload only the freshly-built Conan
# CONAN_PACKAGES is used by Makefile.deploytargs to know which packages should be uploaded
CONAN_PACKAGES_TOREMOVE:=third-party-cpp-libs/*
CONAN_PACKAGES:=third-party-cpp-libs/$(CONAN_VERSION)@empirix/$(CONAN_CHANNEL)

fullpackaging:
	$(MAKE) full_check_deps
	$(MAKE) clean_build_intermediate_artifacts   CFG=release MAKEFLAGS= # remove temp object files and decompressed source folders
	$(MAKE) strip_libs ARCH=docker               CFG=release MAKEFLAGS= # further decrease the size of Conan/RPM packages
	$(MAKE) clean_build_intermediate_artifacts   CFG=debug-gcov   MAKEFLAGS= # remove temp object files and decompressed source folders
	$(MAKE) strip_libs ARCH=docker               CFG=debug-gcov   MAKEFLAGS= # further decrease the size of Conan/RPM packages
	@echo "##teamcity[blockOpened name='Packaging as Conan']"
	conan create   . empirix/$(CONAN_CHANNEL) --settings build_type=Release --options runtime=$(RUNTIME)
	conan create   . empirix/$(CONAN_CHANNEL) --settings build_type=DebugGcov --options runtime=$(RUNTIME)      # FIXME: this last one maybe makes sense to be placed inside the "docker run" step in TC ?
	@echo "##teamcity[blockClosed name='Packaging as Conan']"
	@echo "Packaging completed successfully!"

packaging:
	$(MAKE) full_check_deps
	$(MAKE) clean_build_intermediate_artifacts   MAKEFLAGS= # remove temp object files and decompressed source folders
	$(MAKE) strip_libs ARCH=docker               MAKEFLAGS= # further decrease the size of Conan/RPM packages
	@echo "##teamcity[blockOpened name='Packaging as Conan']"
	conan create   . empirix/$(CONAN_CHANNEL) --settings build_type=$(CONAN_CREATE_BUILD_TYPE) --options runtime=$(RUNTIME)
	@echo "##teamcity[blockClosed name='Packaging as Conan']"
	@echo "Packaging completed successfully!"

packaging_conan:
	$(MAKE) strip_libs ARCH=docker               MAKEFLAGS= # further decrease the size of Conan/RPM packages
	@echo "##teamcity[blockOpened name='Packaging as Conan']"
	conan create   . empirix/$(CONAN_CHANNEL) --settings build_type=$(CONAN_CREATE_BUILD_TYPE) --options runtime=$(RUNTIME)
	@echo "##teamcity[blockClosed name='Packaging as Conan']"
	@echo "Packaging completed successfully!"

# ------------------------------------------------------------------------------------------
# Deploy targets
# ------------------------------------------------------------------------------------------

-include ${EMPIRIX_PIPELINE_FRAMEWORK_MAKEFILE_SUPPORT_DIR}/Deploy-targets.mk

deploy::
	# before uploading Conan source package to the repository, we erase all the sources:
	# we don't use the "build binary from Conan source" feature of Conan and the sources in our case take up to 400MB
	# and they take a lot of time to get uploaded to the repo. So with the following "rm" we actually save quite a bit of disk+time:
	rm -rf /opt/empirix/conan/third-party-cpp-libs/$(CONAN_VERSION)/empirix/$(CONAN_CHANNEL)/export_source/*
	$(MAKE) deploy_conan
	$(MAKE) deploy_rhel7_rpm
	$(MAKE) deploy_rhel8_rpm

# ------------------------------------------------------------------------------------------
# More targets
# ------------------------------------------------------------------------------------------

-include ${EMPIRIX_PIPELINE_FRAMEWORK_MAKEFILE_SUPPORT_DIR}/RootMakefile-targets.mk

# ------------------------------------------------------------------------------------------
# HELP TARGET
# ------------------------------------------------------------------------------------------
help::
	@echo "Recursive targets defined by this Makefile:"
	@echo "  all:              regen symlinks"
	@echo "  packaging:        regen RPMs packages for all 3rd party stuff"
	@echo "  clean_build_intermediate_artifacts:            remove intermediate RPM files"
	@echo "Utility targets defined by this Makefile:"
	@echo "  install_devel_rpm: install locally the devel/runtime RPMs"
	@echo "  install_via_alien: convert RPM to DEB packages and install them locally"
	@echo "  deploy:           deploy RPMs to repositories"
	@echo "  packaging_clean_build_intermediate_artifacts:  remove RPMs to force regeneration later"
